diff --git a/.env.example b/.env.example
index 05a32a5146..be0c04912e 100644
--- a/.env.example
+++ b/.env.example
@@ -11,4 +11,4 @@ DEBUG=TRUE
# Front-End Variables
VITE_BASENAME=/
-#VITE_BACKEND_URL=
+VITE_BACKEND_URL=https://shiny-zebra-wrq6x99qwvjwhvv46-3001.app.github.dev/
diff --git a/Pipfile b/Pipfile
index 4d377014ae..4085ff4201 100644
--- a/Pipfile
+++ b/Pipfile
@@ -12,7 +12,6 @@ flask-migrate = "*"
flask-swagger = "*"
psycopg2-binary = "*"
python-dotenv = "*"
-flask-cors = "*"
gunicorn = "*"
cloudinary = "*"
flask-admin = "==2.0.0"
@@ -20,6 +19,7 @@ typing-extensions = "*"
flask-jwt-extended = "==4.6.0"
wtforms = "==3.1.2"
sqlalchemy = "*"
+flask-cors = "*"
[requires]
python_version = "3.13"
diff --git a/Pipfile.lock b/Pipfile.lock
index d9e474e972..dc7d8b9015 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -42,11 +42,11 @@
},
"click": {
"hashes": [
- "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc",
- "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4"
+ "sha256:14162b8b3b3550a7d479eafa77dfd3c38d9dc8951f6f69c78913a8f9a7540fd5",
+ "sha256:1924d2c27c5653561cd2cae4548d1406039cb79b858b747cfea24924bbc1616d"
],
"markers": "python_version >= '3.10'",
- "version": "==8.3.0"
+ "version": "==8.3.2"
},
"cloudinary": {
"hashes": [
@@ -58,12 +58,12 @@
},
"flask": {
"hashes": [
- "sha256:bf656c15c80190ed628ad08cdfd3aaa35beb087855e2f494910aa3774cc4fd87",
- "sha256:ca1d8112ec8a6158cc29ea4858963350011b5c846a414cdb7a954aa9e967d03c"
+ "sha256:0ef0e52b8a9cd932855379197dd8f94047b359ca0a78695144304cb45f87c9eb",
+ "sha256:f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c"
],
"index": "pypi",
"markers": "python_version >= '3.9'",
- "version": "==3.1.2"
+ "version": "==3.1.3"
},
"flask-admin": {
"hashes": [
@@ -76,12 +76,12 @@
},
"flask-cors": {
"hashes": [
- "sha256:c7b2cbfb1a31aa0d2e5341eea03a6805349f7a61647daee1a15c46bbe981494c",
- "sha256:d81bcb31f07b0985be7f48406247e9243aced229b7747219160a0559edd678db"
+ "sha256:6e118f3698249ae33e429760db98ce032a8bf9913638d085ca0f4c5534ad2423",
+ "sha256:e57544d415dfd7da89a9564e1e3a9e515042df76e12130641ca6f3f2f03b699a"
],
"index": "pypi",
"markers": "python_version >= '3.9' and python_version < '4.0'",
- "version": "==6.0.1"
+ "version": "==6.0.2"
},
"flask-jwt-extended": {
"hashes": [
@@ -575,11 +575,11 @@
},
"werkzeug": {
"hashes": [
- "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e",
- "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746"
+ "sha256:63a77fb8892bf28ebc3178683445222aa500e48ebad5ec77b0ad80f8726b1f50",
+ "sha256:9bad61a4268dac112f1c5cd4630a56ede601b6ed420300677a869083d70a4c44"
],
"markers": "python_version >= '3.9'",
- "version": "==3.1.3"
+ "version": "==3.1.8"
},
"wtforms": {
"hashes": [
diff --git a/README.md b/README.md
index 6b782b220d..3719c58dfd 100644
--- a/README.md
+++ b/README.md
@@ -79,3 +79,7 @@ This boilerplate it's 100% read to deploy with Render.com and Heroku in a matter
This template was built as part of the 4Geeks Academy [Coding Bootcamp](https://4geeksacademy.com/us/coding-bootcamp) by [Alejandro Sanchez](https://twitter.com/alesanchezr) and many other contributors. Find out more about our [Full Stack Developer Course](https://4geeksacademy.com/us/coding-bootcamps/part-time-full-stack-developer), and [Data Science Bootcamp](https://4geeksacademy.com/us/coding-bootcamps/datascience-machine-learning).
You can find other templates and resources like this at the [school github page](https://github.com/4geeksacademy/).
+
+# Elevator Speech:
+*¿Sabías que la falta de comunicación es la mayor causa de estrés entre padres y niñeras?* He creado **Baby Zzzync**, una guía interactiva donde los padres configuran la rutina diaria y reciben notificaciones en vivo conforme la *babysitter* avanza en las tareas. Es una solución digital sencilla que profesionaliza el cuidado del bebé y da paz mental a los padres a través de datos en *tiempo real*.
+**(Paz mental es el producto)**
\ No newline at end of file
diff --git a/dist/index.html b/dist/index.html
index 1e8cb81dfe..3554b14f68 100644
--- a/dist/index.html
+++ b/dist/index.html
@@ -1,14 +1,18 @@
-
-
-
-
-
- Hello Rigo with React + Flux + Context.js
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+ Baby Zzzync
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/index.html b/index.html
index 27a99f796e..fbe6016791 100644
--- a/index.html
+++ b/index.html
@@ -2,11 +2,11 @@
-
- Hello Rigo
+
+ Baby Zzzync
diff --git a/migrations/versions/070195b3b452_.py b/migrations/versions/070195b3b452_.py
new file mode 100644
index 0000000000..3f18f8cb41
--- /dev/null
+++ b/migrations/versions/070195b3b452_.py
@@ -0,0 +1,45 @@
+"""empty message
+
+Revision ID: 070195b3b452
+Revises: 3b972ef6198c
+Create Date: 2026-04-24 21:03:32.758063
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '070195b3b452'
+down_revision = '3b972ef6198c'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('rutina',
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('nombre', sa.String(length=120), nullable=False),
+ sa.Column('detalles', sa.String(length=250), nullable=True),
+ sa.Column('user_id', sa.Integer(), nullable=False),
+ sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_table('actividad',
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('text', sa.String(length=250), nullable=False),
+ sa.Column('time', sa.String(length=50), nullable=False),
+ sa.Column('category', sa.String(length=50), nullable=False),
+ sa.Column('rutina_id', sa.Integer(), nullable=False),
+ sa.ForeignKeyConstraint(['rutina_id'], ['rutina.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_table('actividad')
+ op.drop_table('rutina')
+ # ### end Alembic commands ###
diff --git a/migrations/versions/1d3b2e5ca6a5_.py b/migrations/versions/1d3b2e5ca6a5_.py
new file mode 100644
index 0000000000..e8c86052ba
--- /dev/null
+++ b/migrations/versions/1d3b2e5ca6a5_.py
@@ -0,0 +1,34 @@
+"""empty message
+
+Revision ID: 1d3b2e5ca6a5
+Revises: 070195b3b452
+Create Date: 2026-04-25 17:47:56.374857
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '1d3b2e5ca6a5'
+down_revision = '070195b3b452'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('asignacion_rutina',
+ sa.Column('hijo_id', sa.Integer(), nullable=False),
+ sa.Column('rutina_id', sa.Integer(), nullable=False),
+ sa.ForeignKeyConstraint(['hijo_id'], ['hijo.id'], ),
+ sa.ForeignKeyConstraint(['rutina_id'], ['rutina.id'], ),
+ sa.PrimaryKeyConstraint('hijo_id', 'rutina_id')
+ )
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_table('asignacion_rutina')
+ # ### end Alembic commands ###
diff --git a/migrations/versions/3b972ef6198c_.py b/migrations/versions/3b972ef6198c_.py
new file mode 100644
index 0000000000..63f7c2c5df
--- /dev/null
+++ b/migrations/versions/3b972ef6198c_.py
@@ -0,0 +1,60 @@
+"""empty message
+
+Revision ID: 3b972ef6198c
+Revises: bdc8d44ab3ac
+Create Date: 2026-04-23 09:55:28.882333
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '3b972ef6198c'
+down_revision = 'bdc8d44ab3ac'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('hijo',
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('nombre', sa.String(length=80), nullable=False),
+ sa.Column('apellido', sa.String(length=80), nullable=False),
+ sa.Column('edad', sa.Integer(), nullable=False),
+ sa.Column('foto_url', sa.String(length=255), nullable=True),
+ sa.Column('info_adicional', sa.Text(), nullable=True),
+ sa.Column('intolerancia', sa.String(length=100), nullable=True),
+ sa.Column('alergia', sa.String(length=100), nullable=True),
+ sa.Column('asma', sa.String(length=50), nullable=True),
+ sa.Column('tipo_sangre', sa.String(length=10), nullable=False),
+ sa.Column('gatea', sa.String(length=10), nullable=True),
+ sa.Column('autonomia_bano', sa.String(length=10), nullable=True),
+ sa.Column('user_id', sa.Integer(), nullable=False),
+ sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_table('autorizado',
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('nombre', sa.String(length=80), nullable=False),
+ sa.Column('apellidos', sa.String(length=80), nullable=False),
+ sa.Column('dni', sa.String(length=20), nullable=False),
+ sa.Column('telefono', sa.String(length=20), nullable=False),
+ sa.Column('parentesco', sa.String(length=50), nullable=True),
+ sa.Column('foto_url', sa.String(length=255), nullable=True),
+ sa.Column('es_permanente', sa.Boolean(), nullable=True),
+ sa.Column('valido_desde', sa.String(length=20), nullable=True),
+ sa.Column('valido_hasta', sa.String(length=20), nullable=True),
+ sa.Column('hijo_id', sa.Integer(), nullable=False),
+ sa.ForeignKeyConstraint(['hijo_id'], ['hijo.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_table('autorizado')
+ op.drop_table('hijo')
+ # ### end Alembic commands ###
diff --git a/migrations/versions/ba08d68592c6_.py b/migrations/versions/ba08d68592c6_.py
new file mode 100644
index 0000000000..08567e444b
--- /dev/null
+++ b/migrations/versions/ba08d68592c6_.py
@@ -0,0 +1,37 @@
+"""empty message
+
+Revision ID: ba08d68592c6
+Revises: 1d3b2e5ca6a5
+Create Date: 2026-04-27 07:06:31.747029
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = 'ba08d68592c6'
+down_revision = '1d3b2e5ca6a5'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('rutina_compartida',
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('cuidador_id', sa.Integer(), nullable=False),
+ sa.Column('rutina_id', sa.Integer(), nullable=False),
+ sa.Column('hijo_id', sa.Integer(), nullable=False),
+ sa.ForeignKeyConstraint(['cuidador_id'], ['user.id'], ),
+ sa.ForeignKeyConstraint(['hijo_id'], ['hijo.id'], ),
+ sa.ForeignKeyConstraint(['rutina_id'], ['rutina.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_table('rutina_compartida')
+ # ### end Alembic commands ###
diff --git a/migrations/versions/bdc8d44ab3ac_.py b/migrations/versions/bdc8d44ab3ac_.py
new file mode 100644
index 0000000000..748d79c870
--- /dev/null
+++ b/migrations/versions/bdc8d44ab3ac_.py
@@ -0,0 +1,44 @@
+"""empty message
+
+Revision ID: bdc8d44ab3ac
+Revises: 0763d677d453
+Create Date: 2026-04-14 21:18:15.093608
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = 'bdc8d44ab3ac'
+down_revision = '0763d677d453'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ with op.batch_alter_table('user', schema=None) as batch_op:
+ batch_op.add_column(sa.Column('nombre', sa.String(length=80), nullable=False))
+ batch_op.add_column(sa.Column('apellidos', sa.String(length=80), nullable=False))
+ batch_op.add_column(sa.Column('edad', sa.Integer(), nullable=False))
+ batch_op.add_column(sa.Column('direccion_hogar', sa.String(length=200), nullable=True))
+ batch_op.add_column(sa.Column('direccion_trabajo', sa.String(length=200), nullable=True))
+ batch_op.add_column(sa.Column('telefono', sa.String(length=20), nullable=True))
+ batch_op.add_column(sa.Column('foto_perfil', sa.String(length=255), nullable=True))
+
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ with op.batch_alter_table('user', schema=None) as batch_op:
+ batch_op.drop_column('foto_perfil')
+ batch_op.drop_column('telefono')
+ batch_op.drop_column('direccion_trabajo')
+ batch_op.drop_column('direccion_hogar')
+ batch_op.drop_column('edad')
+ batch_op.drop_column('apellidos')
+ batch_op.drop_column('nombre')
+
+ # ### end Alembic commands ###
diff --git a/package-lock.json b/package-lock.json
index 8d43d98ab7..28f53b3d77 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,10 +9,17 @@
"version": "1.0.1",
"license": "ISC",
"dependencies": {
+ "@emotion/react": "^11.14.0",
+ "@emotion/styled": "^11.14.1",
+ "@mui/material": "^9.0.0",
+ "@mui/x-date-pickers": "^9.0.2",
+ "dayjs": "^1.11.20",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
- "react-router-dom": "^6.18.0"
+ "react-google-autocomplete": "^2.7.5",
+ "react-router-dom": "^6.18.0",
+ "sweetalert2": "^11.26.24"
},
"devDependencies": {
"@types/react": "^18.2.18",
@@ -46,7 +53,6 @@
"version": "7.26.2",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
"integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-validator-identifier": "^7.25.9",
@@ -109,7 +115,6 @@
"version": "7.26.5",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz",
"integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.26.5",
@@ -160,7 +165,6 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz",
"integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/traverse": "^7.25.9",
@@ -202,7 +206,6 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
"integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -212,7 +215,6 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
"integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -246,7 +248,6 @@
"version": "7.26.7",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz",
"integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.26.7"
@@ -290,11 +291,19 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/runtime": {
+ "version": "7.29.2",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz",
+ "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/@babel/template": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz",
"integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.25.9",
@@ -309,7 +318,6 @@
"version": "7.26.7",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz",
"integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.26.2",
@@ -328,7 +336,6 @@
"version": "7.26.7",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz",
"integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.25.9",
@@ -338,6 +345,152 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@emotion/babel-plugin": {
+ "version": "11.13.5",
+ "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
+ "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.16.7",
+ "@babel/runtime": "^7.18.3",
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/serialize": "^1.3.3",
+ "babel-plugin-macros": "^3.1.0",
+ "convert-source-map": "^1.5.0",
+ "escape-string-regexp": "^4.0.0",
+ "find-root": "^1.1.0",
+ "source-map": "^0.5.7",
+ "stylis": "4.2.0"
+ }
+ },
+ "node_modules/@emotion/cache": {
+ "version": "11.14.0",
+ "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz",
+ "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/sheet": "^1.4.0",
+ "@emotion/utils": "^1.4.2",
+ "@emotion/weak-memoize": "^0.4.0",
+ "stylis": "4.2.0"
+ }
+ },
+ "node_modules/@emotion/hash": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
+ "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/is-prop-valid": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz",
+ "integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/memoize": "^0.9.0"
+ }
+ },
+ "node_modules/@emotion/memoize": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
+ "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/react": {
+ "version": "11.14.0",
+ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
+ "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.13.5",
+ "@emotion/cache": "^11.14.0",
+ "@emotion/serialize": "^1.3.3",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
+ "@emotion/utils": "^1.4.2",
+ "@emotion/weak-memoize": "^0.4.0",
+ "hoist-non-react-statics": "^3.3.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@emotion/serialize": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz",
+ "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/unitless": "^0.10.0",
+ "@emotion/utils": "^1.4.2",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@emotion/sheet": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
+ "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/styled": {
+ "version": "11.14.1",
+ "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz",
+ "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.13.5",
+ "@emotion/is-prop-valid": "^1.3.0",
+ "@emotion/serialize": "^1.3.3",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
+ "@emotion/utils": "^1.4.2"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.0.0-rc.0",
+ "react": ">=16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@emotion/unitless": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
+ "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/use-insertion-effect-with-fallbacks": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz",
+ "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@emotion/utils": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz",
+ "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/weak-memoize": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
+ "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==",
+ "license": "MIT"
+ },
"node_modules/@esbuild/android-arm": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
@@ -846,7 +999,6 @@
"version": "0.3.8",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
"integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/set-array": "^1.2.1",
@@ -861,7 +1013,6 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
@@ -871,43 +1022,334 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
- "node_modules/@jridgewell/source-map": {
- "version": "0.3.6",
- "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
- "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@jridgewell/gen-mapping": "^0.3.5",
- "@jridgewell/trace-mapping": "^0.3.25"
- }
- },
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.25",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@mui/core-downloads-tracker": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-9.0.0.tgz",
+ "integrity": "sha512-uwQNGkhv0lf7ufxw6QXev77BW6pWbW+7uxYjU5+rfp4lBkFtMEgJCsarTM3Tn+i0lGx6+Ol2u88JdGXr0GDskA==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ }
+ },
+ "node_modules/@mui/material": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/@mui/material/-/material-9.0.0.tgz",
+ "integrity": "sha512-+VP/oQCDhDR87NQQgXnNBG8dwy6GNuQLnenS1pZvkbn2dKFSxRSRMybTpH9xUxXP+316mlYDy5CSbYtusnCWtw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.29.2",
+ "@mui/core-downloads-tracker": "^9.0.0",
+ "@mui/system": "^9.0.0",
+ "@mui/types": "^9.0.0",
+ "@mui/utils": "^9.0.0",
+ "@popperjs/core": "^2.11.8",
+ "@types/react-transition-group": "^4.4.12",
+ "clsx": "^2.1.1",
+ "csstype": "^3.2.3",
+ "prop-types": "^15.8.1",
+ "react-is": "^19.2.4",
+ "react-transition-group": "^4.4.5"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.5.0",
+ "@emotion/styled": "^11.3.0",
+ "@mui/material-pigment-css": "^9.0.0",
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ },
+ "@mui/material-pigment-css": {
+ "optional": true
+ },
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/material/node_modules/react-is": {
+ "version": "19.2.5",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.5.tgz",
+ "integrity": "sha512-Dn0t8IQhCmeIT3wu+Apm1/YVsJXsGWi6k4sPdnBIdqMVtHtv0IGi6dcpNpNkNac0zB2uUAqNX3MHzN8c+z2rwQ==",
+ "license": "MIT"
+ },
+ "node_modules/@mui/private-theming": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-9.0.0.tgz",
+ "integrity": "sha512-JtuZoaiCqwD6vjgYu6Xp3T7DZkrxJlgtDz5yESzhI34fEX5hHMh2VJUbuL9UOg8xrfIFMrq6dcYoH/7Zi4G0RA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.29.2",
+ "@mui/utils": "^9.0.0",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/styled-engine": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-9.0.0.tgz",
+ "integrity": "sha512-9RLGdX4Jg0aQPRuvqh/OLzYSPlgd5zyEw5/1HIRfdavSiOd03WtUaGZH9/w1RoTYuRKwpgy0hpIFaMHIqPVIWg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.29.2",
+ "@emotion/cache": "^11.14.0",
+ "@emotion/serialize": "^1.3.3",
+ "@emotion/sheet": "^1.4.0",
+ "csstype": "^3.2.3",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.4.1",
+ "@emotion/styled": "^11.3.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/system": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/@mui/system/-/system-9.0.0.tgz",
+ "integrity": "sha512-YnC5Zg6j04IxiLc/boAKs0464jfZlLFVa7mf5E8lF0XOtZVUvG6R6gJK50lgUYdaaLdyLfxF6xR7LaPuEpeT/g==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.29.2",
+ "@mui/private-theming": "^9.0.0",
+ "@mui/styled-engine": "^9.0.0",
+ "@mui/types": "^9.0.0",
+ "@mui/utils": "^9.0.0",
+ "clsx": "^2.1.1",
+ "csstype": "^3.2.3",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.5.0",
+ "@emotion/styled": "^11.3.0",
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ },
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/types": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/@mui/types/-/types-9.0.0.tgz",
+ "integrity": "sha512-i1cuFCAWN44b3AJWO7mh7tuh1sqbQSeVr/94oG0TX5uXivac8XalgE4/6fQZcmGZigzbQ35IXxj/4jLpRIBYZg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.29.2"
+ },
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/utils": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-9.0.0.tgz",
+ "integrity": "sha512-bQcqyg/gjULUqTuyUjSAFr6LQGLvtkNtDbJerAtoUn9kGZ0hg5QJiN1PLHMLbeFpe3te1831uq7GFl2ITokGdg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.29.2",
+ "@mui/types": "^9.0.0",
+ "@types/prop-types": "^15.7.15",
+ "clsx": "^2.1.1",
+ "prop-types": "^15.8.1",
+ "react-is": "^19.2.4"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/utils/node_modules/react-is": {
+ "version": "19.2.5",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.5.tgz",
+ "integrity": "sha512-Dn0t8IQhCmeIT3wu+Apm1/YVsJXsGWi6k4sPdnBIdqMVtHtv0IGi6dcpNpNkNac0zB2uUAqNX3MHzN8c+z2rwQ==",
+ "license": "MIT"
+ },
+ "node_modules/@mui/x-date-pickers": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-9.0.2.tgz",
+ "integrity": "sha512-rnyc2wFPnprTS5i8Lq9aX2Rlx+ZRvNZERTd7sPMErf/8HnMCYzxwErITbd+TuImlvo2vaLF1gNynqT8AJMusYw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.28.6",
+ "@mui/utils": "9.0.0",
+ "@mui/x-internals": "^9.0.0",
+ "@types/react-transition-group": "^4.4.12",
+ "clsx": "^2.1.1",
+ "prop-types": "^15.8.1",
+ "react-transition-group": "^4.4.5"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.9.0",
+ "@emotion/styled": "^11.8.1",
+ "@mui/material": "^7.3.0 || ^9.0.0",
+ "@mui/system": "^7.3.0 || ^9.0.0",
+ "date-fns": "^2.25.0 || ^3.2.0 || ^4.0.0",
+ "date-fns-jalali": "^2.13.0-0 || ^3.2.0-0 || ^4.0.0-0",
+ "dayjs": "^1.10.7",
+ "luxon": "^3.0.2",
+ "moment": "^2.29.4",
+ "moment-hijri": "^2.1.2 || ^3.0.0",
+ "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ },
+ "date-fns": {
+ "optional": true
+ },
+ "date-fns-jalali": {
+ "optional": true
+ },
+ "dayjs": {
+ "optional": true
+ },
+ "luxon": {
+ "optional": true
+ },
+ "moment": {
+ "optional": true
+ },
+ "moment-hijri": {
+ "optional": true
+ },
+ "moment-jalaali": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/x-internals": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-9.0.0.tgz",
+ "integrity": "sha512-E/4rdg69JjhyybpPGypCjAKSKLLnSdCFM+O6P/nkUg47+qt3uftxQEhjQO53rcn6ahHl6du/uNZ9BLgeY6kYxQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.28.6",
+ "@mui/utils": "9.0.0",
+ "reselect": "^5.1.1",
+ "use-sync-external-store": "^1.6.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -943,6 +1385,16 @@
"node": ">= 8"
}
},
+ "node_modules/@popperjs/core": {
+ "version": "2.11.8",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/popperjs"
+ }
+ },
"node_modules/@remix-run/router": {
"version": "1.22.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.22.0.tgz",
@@ -997,26 +1449,22 @@
"@babel/types": "^7.20.7"
}
},
- "node_modules/@types/node": {
- "version": "16.11.12",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.12.tgz",
- "integrity": "sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw==",
- "dev": true,
- "optional": true,
- "peer": true
+ "node_modules/@types/parse-json": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
+ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
+ "license": "MIT"
},
"node_modules/@types/prop-types": {
- "version": "15.7.14",
- "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz",
- "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==",
- "dev": true,
+ "version": "15.7.15",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
+ "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
"license": "MIT"
},
"node_modules/@types/react": {
"version": "18.3.18",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz",
"integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@types/prop-types": "*",
@@ -1033,6 +1481,15 @@
"@types/react": "^18.0.0"
}
},
+ "node_modules/@types/react-transition-group": {
+ "version": "4.4.12",
+ "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
+ "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*"
+ }
+ },
"node_modules/@ungap/structured-clone": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
@@ -1258,6 +1715,21 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/babel-plugin-macros": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
+ "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.12.5",
+ "cosmiconfig": "^7.0.0",
+ "resolve": "^1.19.0"
+ },
+ "engines": {
+ "node": ">=10",
+ "npm": ">=6"
+ }
+ },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -1307,14 +1779,6 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
- "node_modules/buffer-from": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
- "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
- "dev": true,
- "optional": true,
- "peer": true
- },
"node_modules/call-bind": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
@@ -1369,7 +1833,6 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
- "dev": true,
"engines": {
"node": ">=6"
}
@@ -1395,12 +1858,43 @@
],
"license": "CC-BY-4.0"
},
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true
},
+ "node_modules/convert-source-map": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
+ "license": "MIT"
+ },
+ "node_modules/cosmiconfig": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
+ "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/parse-json": "^4.0.0",
+ "import-fresh": "^3.2.1",
+ "parse-json": "^5.0.0",
+ "path-type": "^4.0.0",
+ "yaml": "^1.10.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -1417,10 +1911,9 @@
}
},
"node_modules/csstype": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
- "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
- "dev": true,
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"license": "MIT"
},
"node_modules/data-view-buffer": {
@@ -1477,11 +1970,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/dayjs": {
+ "version": "1.11.20",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.20.tgz",
+ "integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==",
+ "license": "MIT"
+ },
"node_modules/debug": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
- "dev": true,
"dependencies": {
"ms": "2.1.2"
},
@@ -1549,6 +2047,16 @@
"node": ">=6.0.0"
}
},
+ "node_modules/dom-helpers": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+ "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.8.7",
+ "csstype": "^3.0.2"
+ }
+ },
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -1571,6 +2079,15 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/error-ex": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
+ "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
+ "license": "MIT",
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
"node_modules/es-abstract": {
"version": "1.23.9",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz",
@@ -1651,7 +2168,6 @@
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -1790,6 +2306,18 @@
"node": ">=6"
}
},
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/eslint": {
"version": "8.57.1",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
@@ -1999,18 +2527,6 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
- "node_modules/eslint/node_modules/escape-string-regexp": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
- "dev": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/eslint/node_modules/eslint-visitor-keys": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
@@ -2252,6 +2768,12 @@
"node": "^10.12.0 || >=12.0.0"
}
},
+ "node_modules/find-root": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
+ "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==",
+ "license": "MIT"
+ },
"node_modules/flat-cache": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
@@ -2311,7 +2833,6 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
- "dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -2438,7 +2959,6 @@
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
- "dev": true,
"engines": {
"node": ">=4"
}
@@ -2555,7 +3075,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
@@ -2564,6 +3083,15 @@
"node": ">= 0.4"
}
},
+ "node_modules/hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "react-is": "^16.7.0"
+ }
+ },
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -2578,7 +3106,6 @@
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
- "dev": true,
"dependencies": {
"parent-module": "^1.0.0",
"resolve-from": "^4.0.0"
@@ -2648,6 +3175,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+ "license": "MIT"
+ },
"node_modules/is-async-function": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
@@ -2718,7 +3251,6 @@
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
- "dev": true,
"license": "MIT",
"dependencies": {
"hasown": "^2.0.2"
@@ -3052,7 +3584,6 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
- "dev": true,
"license": "MIT",
"bin": {
"jsesc": "bin/jsesc"
@@ -3061,6 +3592,12 @@
"node": ">=6"
}
},
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+ "license": "MIT"
+ },
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
@@ -3113,6 +3650,18 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.debounce": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
+ "license": "MIT"
+ },
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -3156,8 +3705,7 @@
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/nanoid": {
"version": "3.3.8",
@@ -3344,7 +3892,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
- "dev": true,
"dependencies": {
"callsites": "^3.0.0"
},
@@ -3352,6 +3899,24 @@
"node": ">=6"
}
},
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -3382,14 +3947,21 @@
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
- "dev": true
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
+ },
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
- "dev": true,
"license": "ISC"
},
"node_modules/possible-typed-array-names": {
@@ -3506,6 +4078,19 @@
"react": "^18.3.1"
}
},
+ "node_modules/react-google-autocomplete": {
+ "version": "2.7.5",
+ "resolved": "https://registry.npmjs.org/react-google-autocomplete/-/react-google-autocomplete-2.7.5.tgz",
+ "integrity": "sha512-CPHMFBCgaJER19iA/y9Isjxh5au8mRxkt6przu9miOHLqrno+ifwJGXQOzMvtwdqNPXhb5Z6jpTtiJrbFeUvHg==",
+ "license": "ISC",
+ "dependencies": {
+ "lodash.debounce": "^4.0.8",
+ "prop-types": "^15.5.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
@@ -3553,6 +4138,22 @@
"react-dom": ">=16.8"
}
},
+ "node_modules/react-transition-group": {
+ "version": "4.4.5",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
+ "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@babel/runtime": "^7.5.5",
+ "dom-helpers": "^5.0.1",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.6.2"
+ },
+ "peerDependencies": {
+ "react": ">=16.6.0",
+ "react-dom": ">=16.6.0"
+ }
+ },
"node_modules/reflect.getprototypeof": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
@@ -3586,9 +4187,36 @@
"call-bind": "^1.0.8",
"define-properties": "^1.2.1",
"es-errors": "^1.3.0",
- "get-proto": "^1.0.1",
- "gopd": "^1.2.0",
- "set-function-name": "^2.0.2"
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "set-function-name": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/reselect": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
+ "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==",
+ "license": "MIT"
+ },
+ "node_modules/resolve": {
+ "version": "1.22.12",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz",
+ "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "is-core-module": "^2.16.1",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
},
"engines": {
"node": ">= 0.4"
@@ -3601,7 +4229,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
- "dev": true,
"engines": {
"node": ">=4"
}
@@ -3905,35 +4532,21 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/source-map-js": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
- "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
- "dev": true,
+ "node_modules/source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
- "node_modules/source-map-support": {
- "version": "0.5.21",
- "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
- "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
- "dev": true,
- "optional": true,
- "peer": true,
- "dependencies": {
- "buffer-from": "^1.0.0",
- "source-map": "^0.6.0"
- }
- },
- "node_modules/source-map-support/node_modules/source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
- "optional": true,
- "peer": true,
+ "license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
@@ -4061,11 +4674,16 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/stylis": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
+ "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==",
+ "license": "MIT"
+ },
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -4074,35 +4692,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/terser": {
- "version": "5.38.1",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.38.1.tgz",
- "integrity": "sha512-GWANVlPM/ZfYzuPHjq0nxT+EbOEDDN3Jwhwdg1D8TU8oSkktp8w64Uq4auuGLxFSoNTRDncTq2hQHX1Ld9KHkA==",
- "dev": true,
- "license": "BSD-2-Clause",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@jridgewell/source-map": "^0.3.3",
- "acorn": "^8.8.2",
- "commander": "^2.20.0",
- "source-map-support": "~0.5.20"
- },
- "bin": {
- "terser": "bin/terser"
- },
- "engines": {
- "node": ">=10"
+ "node_modules/sweetalert2": {
+ "version": "11.26.24",
+ "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.26.24.tgz",
+ "integrity": "sha512-SLgukW4wicewpW5VOukSXY5Z6DL/z7HCOK2ODSjmQPiSphCN8gJAmh9npoceXOtBRNoDN0xIz+zHYthtfiHmjg==",
+ "license": "MIT",
+ "funding": {
+ "type": "individual",
+ "url": "https://github.com/sponsors/limonte"
}
},
- "node_modules/terser/node_modules/commander": {
- "version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
- "dev": true,
- "optional": true,
- "peer": true
- },
"node_modules/text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
@@ -4272,6 +4871,15 @@
"punycode": "^2.1.0"
}
},
+ "node_modules/use-sync-external-store": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
+ "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/vite": {
"version": "4.5.9",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.9.tgz",
@@ -4454,6 +5062,15 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
},
+ "node_modules/yaml": {
+ "version": "1.10.3",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz",
+ "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
@@ -4482,7 +5099,6 @@
"version": "7.26.2",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
"integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
- "dev": true,
"requires": {
"@babel/helper-validator-identifier": "^7.25.9",
"js-tokens": "^4.0.0",
@@ -4530,7 +5146,6 @@
"version": "7.26.5",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz",
"integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==",
- "dev": true,
"requires": {
"@babel/parser": "^7.26.5",
"@babel/types": "^7.26.5",
@@ -4573,7 +5188,6 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz",
"integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==",
- "dev": true,
"requires": {
"@babel/traverse": "^7.25.9",
"@babel/types": "^7.25.9"
@@ -4599,14 +5213,12 @@
"@babel/helper-string-parser": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
- "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
- "dev": true
+ "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA=="
},
"@babel/helper-validator-identifier": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
- "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
- "dev": true
+ "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="
},
"@babel/helper-validator-option": {
"version": "7.25.9",
@@ -4628,7 +5240,6 @@
"version": "7.26.7",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz",
"integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==",
- "dev": true,
"requires": {
"@babel/types": "^7.26.7"
}
@@ -4651,11 +5262,15 @@
"@babel/helper-plugin-utils": "^7.25.9"
}
},
+ "@babel/runtime": {
+ "version": "7.29.2",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz",
+ "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g=="
+ },
"@babel/template": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz",
"integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==",
- "dev": true,
"requires": {
"@babel/code-frame": "^7.25.9",
"@babel/parser": "^7.25.9",
@@ -4666,7 +5281,6 @@
"version": "7.26.7",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz",
"integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==",
- "dev": true,
"requires": {
"@babel/code-frame": "^7.26.2",
"@babel/generator": "^7.26.5",
@@ -4681,12 +5295,125 @@
"version": "7.26.7",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz",
"integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==",
- "dev": true,
"requires": {
"@babel/helper-string-parser": "^7.25.9",
"@babel/helper-validator-identifier": "^7.25.9"
}
},
+ "@emotion/babel-plugin": {
+ "version": "11.13.5",
+ "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
+ "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==",
+ "requires": {
+ "@babel/helper-module-imports": "^7.16.7",
+ "@babel/runtime": "^7.18.3",
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/serialize": "^1.3.3",
+ "babel-plugin-macros": "^3.1.0",
+ "convert-source-map": "^1.5.0",
+ "escape-string-regexp": "^4.0.0",
+ "find-root": "^1.1.0",
+ "source-map": "^0.5.7",
+ "stylis": "4.2.0"
+ }
+ },
+ "@emotion/cache": {
+ "version": "11.14.0",
+ "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz",
+ "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==",
+ "requires": {
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/sheet": "^1.4.0",
+ "@emotion/utils": "^1.4.2",
+ "@emotion/weak-memoize": "^0.4.0",
+ "stylis": "4.2.0"
+ }
+ },
+ "@emotion/hash": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
+ "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g=="
+ },
+ "@emotion/is-prop-valid": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz",
+ "integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==",
+ "requires": {
+ "@emotion/memoize": "^0.9.0"
+ }
+ },
+ "@emotion/memoize": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
+ "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ=="
+ },
+ "@emotion/react": {
+ "version": "11.14.0",
+ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
+ "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
+ "requires": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.13.5",
+ "@emotion/cache": "^11.14.0",
+ "@emotion/serialize": "^1.3.3",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
+ "@emotion/utils": "^1.4.2",
+ "@emotion/weak-memoize": "^0.4.0",
+ "hoist-non-react-statics": "^3.3.1"
+ }
+ },
+ "@emotion/serialize": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz",
+ "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==",
+ "requires": {
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/unitless": "^0.10.0",
+ "@emotion/utils": "^1.4.2",
+ "csstype": "^3.0.2"
+ }
+ },
+ "@emotion/sheet": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
+ "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg=="
+ },
+ "@emotion/styled": {
+ "version": "11.14.1",
+ "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz",
+ "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==",
+ "requires": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.13.5",
+ "@emotion/is-prop-valid": "^1.3.0",
+ "@emotion/serialize": "^1.3.3",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
+ "@emotion/utils": "^1.4.2"
+ }
+ },
+ "@emotion/unitless": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
+ "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg=="
+ },
+ "@emotion/use-insertion-effect-with-fallbacks": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz",
+ "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==",
+ "requires": {}
+ },
+ "@emotion/utils": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz",
+ "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA=="
+ },
+ "@emotion/weak-memoize": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
+ "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg=="
+ },
"@esbuild/android-arm": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
@@ -4925,7 +5652,6 @@
"version": "0.3.8",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
"integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
- "dev": true,
"requires": {
"@jridgewell/set-array": "^1.2.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
@@ -4935,43 +5661,149 @@
"@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
- "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
- "dev": true
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="
},
"@jridgewell/set-array": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
- "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
- "dev": true
- },
- "@jridgewell/source-map": {
- "version": "0.3.6",
- "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
- "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
- "dev": true,
- "optional": true,
- "peer": true,
- "requires": {
- "@jridgewell/gen-mapping": "^0.3.5",
- "@jridgewell/trace-mapping": "^0.3.25"
- }
+ "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="
},
"@jridgewell/sourcemap-codec": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
- "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
- "dev": true
+ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
},
"@jridgewell/trace-mapping": {
"version": "0.3.25",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
- "dev": true,
"requires": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "@mui/core-downloads-tracker": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-9.0.0.tgz",
+ "integrity": "sha512-uwQNGkhv0lf7ufxw6QXev77BW6pWbW+7uxYjU5+rfp4lBkFtMEgJCsarTM3Tn+i0lGx6+Ol2u88JdGXr0GDskA=="
+ },
+ "@mui/material": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/@mui/material/-/material-9.0.0.tgz",
+ "integrity": "sha512-+VP/oQCDhDR87NQQgXnNBG8dwy6GNuQLnenS1pZvkbn2dKFSxRSRMybTpH9xUxXP+316mlYDy5CSbYtusnCWtw==",
+ "requires": {
+ "@babel/runtime": "^7.29.2",
+ "@mui/core-downloads-tracker": "^9.0.0",
+ "@mui/system": "^9.0.0",
+ "@mui/types": "^9.0.0",
+ "@mui/utils": "^9.0.0",
+ "@popperjs/core": "^2.11.8",
+ "@types/react-transition-group": "^4.4.12",
+ "clsx": "^2.1.1",
+ "csstype": "^3.2.3",
+ "prop-types": "^15.8.1",
+ "react-is": "^19.2.4",
+ "react-transition-group": "^4.4.5"
+ },
+ "dependencies": {
+ "react-is": {
+ "version": "19.2.5",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.5.tgz",
+ "integrity": "sha512-Dn0t8IQhCmeIT3wu+Apm1/YVsJXsGWi6k4sPdnBIdqMVtHtv0IGi6dcpNpNkNac0zB2uUAqNX3MHzN8c+z2rwQ=="
+ }
+ }
+ },
+ "@mui/private-theming": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-9.0.0.tgz",
+ "integrity": "sha512-JtuZoaiCqwD6vjgYu6Xp3T7DZkrxJlgtDz5yESzhI34fEX5hHMh2VJUbuL9UOg8xrfIFMrq6dcYoH/7Zi4G0RA==",
+ "requires": {
+ "@babel/runtime": "^7.29.2",
+ "@mui/utils": "^9.0.0",
+ "prop-types": "^15.8.1"
+ }
+ },
+ "@mui/styled-engine": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-9.0.0.tgz",
+ "integrity": "sha512-9RLGdX4Jg0aQPRuvqh/OLzYSPlgd5zyEw5/1HIRfdavSiOd03WtUaGZH9/w1RoTYuRKwpgy0hpIFaMHIqPVIWg==",
+ "requires": {
+ "@babel/runtime": "^7.29.2",
+ "@emotion/cache": "^11.14.0",
+ "@emotion/serialize": "^1.3.3",
+ "@emotion/sheet": "^1.4.0",
+ "csstype": "^3.2.3",
+ "prop-types": "^15.8.1"
+ }
+ },
+ "@mui/system": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/@mui/system/-/system-9.0.0.tgz",
+ "integrity": "sha512-YnC5Zg6j04IxiLc/boAKs0464jfZlLFVa7mf5E8lF0XOtZVUvG6R6gJK50lgUYdaaLdyLfxF6xR7LaPuEpeT/g==",
+ "requires": {
+ "@babel/runtime": "^7.29.2",
+ "@mui/private-theming": "^9.0.0",
+ "@mui/styled-engine": "^9.0.0",
+ "@mui/types": "^9.0.0",
+ "@mui/utils": "^9.0.0",
+ "clsx": "^2.1.1",
+ "csstype": "^3.2.3",
+ "prop-types": "^15.8.1"
+ }
+ },
+ "@mui/types": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/@mui/types/-/types-9.0.0.tgz",
+ "integrity": "sha512-i1cuFCAWN44b3AJWO7mh7tuh1sqbQSeVr/94oG0TX5uXivac8XalgE4/6fQZcmGZigzbQ35IXxj/4jLpRIBYZg==",
+ "requires": {
+ "@babel/runtime": "^7.29.2"
+ }
+ },
+ "@mui/utils": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-9.0.0.tgz",
+ "integrity": "sha512-bQcqyg/gjULUqTuyUjSAFr6LQGLvtkNtDbJerAtoUn9kGZ0hg5QJiN1PLHMLbeFpe3te1831uq7GFl2ITokGdg==",
+ "requires": {
+ "@babel/runtime": "^7.29.2",
+ "@mui/types": "^9.0.0",
+ "@types/prop-types": "^15.7.15",
+ "clsx": "^2.1.1",
+ "prop-types": "^15.8.1",
+ "react-is": "^19.2.4"
+ },
+ "dependencies": {
+ "react-is": {
+ "version": "19.2.5",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.5.tgz",
+ "integrity": "sha512-Dn0t8IQhCmeIT3wu+Apm1/YVsJXsGWi6k4sPdnBIdqMVtHtv0IGi6dcpNpNkNac0zB2uUAqNX3MHzN8c+z2rwQ=="
+ }
+ }
+ },
+ "@mui/x-date-pickers": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-9.0.2.tgz",
+ "integrity": "sha512-rnyc2wFPnprTS5i8Lq9aX2Rlx+ZRvNZERTd7sPMErf/8HnMCYzxwErITbd+TuImlvo2vaLF1gNynqT8AJMusYw==",
+ "requires": {
+ "@babel/runtime": "^7.28.6",
+ "@mui/utils": "9.0.0",
+ "@mui/x-internals": "^9.0.0",
+ "@types/react-transition-group": "^4.4.12",
+ "clsx": "^2.1.1",
+ "prop-types": "^15.8.1",
+ "react-transition-group": "^4.4.5"
+ }
+ },
+ "@mui/x-internals": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-9.0.0.tgz",
+ "integrity": "sha512-E/4rdg69JjhyybpPGypCjAKSKLLnSdCFM+O6P/nkUg47+qt3uftxQEhjQO53rcn6ahHl6du/uNZ9BLgeY6kYxQ==",
+ "requires": {
+ "@babel/runtime": "^7.28.6",
+ "@mui/utils": "9.0.0",
+ "reselect": "^5.1.1",
+ "use-sync-external-store": "^1.6.0"
+ }
+ },
"@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -4998,6 +5830,11 @@
"fastq": "^1.6.0"
}
},
+ "@popperjs/core": {
+ "version": "2.11.8",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="
+ },
"@remix-run/router": {
"version": "1.22.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.22.0.tgz",
@@ -5044,25 +5881,20 @@
"@babel/types": "^7.20.7"
}
},
- "@types/node": {
- "version": "16.11.12",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.12.tgz",
- "integrity": "sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw==",
- "dev": true,
- "optional": true,
- "peer": true
+ "@types/parse-json": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
+ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="
},
"@types/prop-types": {
- "version": "15.7.14",
- "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz",
- "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==",
- "dev": true
+ "version": "15.7.15",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
+ "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="
},
"@types/react": {
"version": "18.3.18",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz",
"integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==",
- "dev": true,
"requires": {
"@types/prop-types": "*",
"csstype": "^3.0.2"
@@ -5075,6 +5907,12 @@
"dev": true,
"requires": {}
},
+ "@types/react-transition-group": {
+ "version": "4.4.12",
+ "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
+ "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==",
+ "requires": {}
+ },
"@ungap/structured-clone": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
@@ -5224,6 +6062,16 @@
"possible-typed-array-names": "^1.0.0"
}
},
+ "babel-plugin-macros": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
+ "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
+ "requires": {
+ "@babel/runtime": "^7.12.5",
+ "cosmiconfig": "^7.0.0",
+ "resolve": "^1.19.0"
+ }
+ },
"balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -5252,14 +6100,6 @@
"update-browserslist-db": "^1.1.1"
}
},
- "buffer-from": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
- "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
- "dev": true,
- "optional": true,
- "peer": true
- },
"call-bind": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
@@ -5295,8 +6135,7 @@
"callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
- "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
- "dev": true
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
},
"caniuse-lite": {
"version": "1.0.30001697",
@@ -5304,12 +6143,34 @@
"integrity": "sha512-GwNPlWJin8E+d7Gxq96jxM6w0w+VFeyyXRsjU58emtkYqnbwHqXm5uT2uCmO0RQE9htWknOP4xtBlLmM/gWxvQ==",
"dev": true
},
+ "clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="
+ },
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true
},
+ "convert-source-map": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
+ },
+ "cosmiconfig": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
+ "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
+ "requires": {
+ "@types/parse-json": "^4.0.0",
+ "import-fresh": "^3.2.1",
+ "parse-json": "^5.0.0",
+ "path-type": "^4.0.0",
+ "yaml": "^1.10.0"
+ }
+ },
"cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -5322,10 +6183,9 @@
}
},
"csstype": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
- "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
- "dev": true
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="
},
"data-view-buffer": {
"version": "1.0.2",
@@ -5360,11 +6220,15 @@
"is-data-view": "^1.0.1"
}
},
+ "dayjs": {
+ "version": "1.11.20",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.20.tgz",
+ "integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ=="
+ },
"debug": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
- "dev": true,
"requires": {
"ms": "2.1.2"
}
@@ -5406,6 +6270,15 @@
"esutils": "^2.0.2"
}
},
+ "dom-helpers": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+ "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+ "requires": {
+ "@babel/runtime": "^7.8.7",
+ "csstype": "^3.0.2"
+ }
+ },
"dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -5423,6 +6296,14 @@
"integrity": "sha512-M+29jTcfNNoR9NV7la4SwUqzWAxEwnc7ThA5e1m6LRSotmpfpCpLcIfgtSCVL+MllNLgAyM/5ru86iMRemPzDQ==",
"dev": true
},
+ "error-ex": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
+ "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
+ "requires": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
"es-abstract": {
"version": "1.23.9",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz",
@@ -5491,8 +6372,7 @@
"es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
- "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
- "dev": true
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="
},
"es-iterator-helpers": {
"version": "1.2.1",
@@ -5595,6 +6475,11 @@
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"dev": true
},
+ "escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
+ },
"eslint": {
"version": "8.57.1",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
@@ -5675,12 +6560,6 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
- "escape-string-regexp": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
- "dev": true
- },
"eslint-visitor-keys": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
@@ -5916,6 +6795,11 @@
"flat-cache": "^3.0.4"
}
},
+ "find-root": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
+ "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
+ },
"flat-cache": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
@@ -5957,8 +6841,7 @@
"function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
- "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
- "dev": true
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
},
"function.prototype.name": {
"version": "1.1.8",
@@ -6042,8 +6925,7 @@
"globals": {
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
- "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
- "dev": true
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="
},
"globalthis": {
"version": "1.0.4",
@@ -6110,11 +6992,18 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
- "dev": true,
"requires": {
"function-bind": "^1.1.2"
}
},
+ "hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "requires": {
+ "react-is": "^16.7.0"
+ }
+ },
"ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -6125,7 +7014,6 @@
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
- "dev": true,
"requires": {
"parent-module": "^1.0.0",
"resolve-from": "^4.0.0"
@@ -6175,6 +7063,11 @@
"get-intrinsic": "^1.2.6"
}
},
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
+ },
"is-async-function": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
@@ -6217,7 +7110,6 @@
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
- "dev": true,
"requires": {
"hasown": "^2.0.2"
}
@@ -6420,8 +7312,12 @@
"jsesc": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
- "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
- "dev": true
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="
+ },
+ "json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
},
"json-schema-traverse": {
"version": "0.4.1",
@@ -6461,6 +7357,16 @@
"type-check": "~0.4.0"
}
},
+ "lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
+ },
+ "lodash.debounce": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
+ },
"lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -6493,8 +7399,7 @@
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"nanoid": {
"version": "3.3.8",
@@ -6618,11 +7523,21 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
- "dev": true,
"requires": {
"callsites": "^3.0.0"
}
},
+ "parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ }
+ },
"path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -6644,14 +7559,17 @@
"path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
- "dev": true
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
+ },
+ "path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="
},
"picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
- "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
- "dev": true
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
},
"possible-typed-array-names": {
"version": "1.0.0",
@@ -6715,6 +7633,15 @@
"scheduler": "^0.23.2"
}
},
+ "react-google-autocomplete": {
+ "version": "2.7.5",
+ "resolved": "https://registry.npmjs.org/react-google-autocomplete/-/react-google-autocomplete-2.7.5.tgz",
+ "integrity": "sha512-CPHMFBCgaJER19iA/y9Isjxh5au8mRxkt6przu9miOHLqrno+ifwJGXQOzMvtwdqNPXhb5Z6jpTtiJrbFeUvHg==",
+ "requires": {
+ "lodash.debounce": "^4.0.8",
+ "prop-types": "^15.5.0"
+ }
+ },
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
@@ -6743,6 +7670,17 @@
"react-router": "6.29.0"
}
},
+ "react-transition-group": {
+ "version": "4.4.5",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
+ "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
+ "requires": {
+ "@babel/runtime": "^7.5.5",
+ "dom-helpers": "^5.0.1",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.6.2"
+ }
+ },
"reflect.getprototypeof": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
@@ -6773,11 +7711,26 @@
"set-function-name": "^2.0.2"
}
},
+ "reselect": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
+ "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="
+ },
+ "resolve": {
+ "version": "1.22.12",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz",
+ "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==",
+ "requires": {
+ "es-errors": "^1.3.0",
+ "is-core-module": "^2.16.1",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ }
+ },
"resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
- "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
- "dev": true
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
},
"reusify": {
"version": "1.0.4",
@@ -6976,34 +7929,17 @@
"side-channel-map": "^1.0.1"
}
},
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="
+ },
"source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true
},
- "source-map-support": {
- "version": "0.5.21",
- "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
- "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
- "dev": true,
- "optional": true,
- "peer": true,
- "requires": {
- "buffer-from": "^1.0.0",
- "source-map": "^0.6.0"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true,
- "optional": true,
- "peer": true
- }
- }
- },
"string.prototype.matchall": {
"version": "4.0.12",
"resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz",
@@ -7088,35 +8024,20 @@
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
"dev": true
},
+ "stylis": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
+ "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="
+ },
"supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
- "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
- "dev": true
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
},
- "terser": {
- "version": "5.38.1",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.38.1.tgz",
- "integrity": "sha512-GWANVlPM/ZfYzuPHjq0nxT+EbOEDDN3Jwhwdg1D8TU8oSkktp8w64Uq4auuGLxFSoNTRDncTq2hQHX1Ld9KHkA==",
- "dev": true,
- "optional": true,
- "peer": true,
- "requires": {
- "@jridgewell/source-map": "^0.3.3",
- "acorn": "^8.8.2",
- "commander": "^2.20.0",
- "source-map-support": "~0.5.20"
- },
- "dependencies": {
- "commander": {
- "version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
- "dev": true,
- "optional": true,
- "peer": true
- }
- }
+ "sweetalert2": {
+ "version": "11.26.24",
+ "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.26.24.tgz",
+ "integrity": "sha512-SLgukW4wicewpW5VOukSXY5Z6DL/z7HCOK2ODSjmQPiSphCN8gJAmh9npoceXOtBRNoDN0xIz+zHYthtfiHmjg=="
},
"text-table": {
"version": "0.2.0",
@@ -7223,6 +8144,12 @@
"punycode": "^2.1.0"
}
},
+ "use-sync-external-store": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
+ "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
+ "requires": {}
+ },
"vite": {
"version": "4.5.9",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.9.tgz",
@@ -7324,6 +8251,11 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
},
+ "yaml": {
+ "version": "1.10.3",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz",
+ "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA=="
+ },
"yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
diff --git a/package.json b/package.json
index 0caab10749..31bc9718bb 100755
--- a/package.json
+++ b/package.json
@@ -8,10 +8,10 @@
"main": "index.js",
"scripts": {
"dev": "vite",
- "start": "vite",
- "build": "vite build",
- "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
- "preview": "vite preview"
+ "start": "vite",
+ "build": "vite build",
+ "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
+ "preview": "vite preview"
},
"author": {
"name": "Alejandro Sanchez",
@@ -30,13 +30,13 @@
"license": "ISC",
"devDependencies": {
"@types/react": "^18.2.18",
- "@types/react-dom": "^18.2.7",
- "@vitejs/plugin-react": "^4.0.4",
- "eslint": "^8.46.0",
- "eslint-plugin-react": "^7.33.1",
- "eslint-plugin-react-hooks": "^4.6.0",
- "eslint-plugin-react-refresh": "^0.4.3",
- "vite": "^4.4.8"
+ "@types/react-dom": "^18.2.7",
+ "@vitejs/plugin-react": "^4.0.4",
+ "eslint": "^8.46.0",
+ "eslint-plugin-react": "^7.33.1",
+ "eslint-plugin-react-hooks": "^4.6.0",
+ "eslint-plugin-react-refresh": "^0.4.3",
+ "vite": "^4.4.8"
},
"babel": {
"presets": [
@@ -54,9 +54,16 @@
]
},
"dependencies": {
+ "@emotion/react": "^11.14.0",
+ "@emotion/styled": "^11.14.1",
+ "@mui/material": "^9.0.0",
+ "@mui/x-date-pickers": "^9.0.2",
+ "dayjs": "^1.11.20",
"prop-types": "^15.8.1",
- "react": "^18.2.0",
- "react-dom": "^18.2.0",
- "react-router-dom": "^6.18.0"
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-google-autocomplete": "^2.7.5",
+ "react-router-dom": "^6.18.0",
+ "sweetalert2": "^11.26.24"
}
}
diff --git a/public/4geeks.ico b/public/4geeks.jpg
similarity index 100%
rename from public/4geeks.ico
rename to public/4geeks.jpg
diff --git a/public/Favicon.png b/public/Favicon.png
new file mode 100644
index 0000000000..ac072aedf9
Binary files /dev/null and b/public/Favicon.png differ
diff --git a/render_build.sh b/render_build.sh
index 404821a383..f5456a1397 100644
--- a/render_build.sh
+++ b/render_build.sh
@@ -5,6 +5,9 @@ set -o errexit
npm install
npm run build
-pipenv install
+pip install --upgrade pip
+pip install pipenv
+
+pipenv install --dev --deploy --ignore-pipfile
pipenv run upgrade
diff --git a/src/api/models.py b/src/api/models.py
index da515f6a1a..a48fc5c73f 100644
--- a/src/api/models.py
+++ b/src/api/models.py
@@ -1,19 +1,155 @@
from flask_sqlalchemy import SQLAlchemy
-from sqlalchemy import String, Boolean
-from sqlalchemy.orm import Mapped, mapped_column
db = SQLAlchemy()
-class User(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(nullable=False)
- is_active: Mapped[bool] = mapped_column(Boolean(), nullable=False)
+asignacion_rutina = db.Table('asignacion_rutina',
+ db.Column('hijo_id', db.Integer, db.ForeignKey('hijo.id'), primary_key=True),
+ db.Column('rutina_id', db.Integer, db.ForeignKey('rutina.id'), primary_key=True)
+)
+class User(db.Model):
+ __tablename__ = 'user'
+ id = db.Column(db.Integer, primary_key=True)
+ email = db.Column(db.String(120), unique=True, nullable=False)
+ password = db.Column(db.String(80), unique=False, nullable=False)
+ nombre = db.Column(db.String(80), nullable=False)
+ apellidos = db.Column(db.String(80), nullable=False)
+ edad = db.Column(db.Integer, nullable=False)
+ direccion_hogar = db.Column(db.String(200))
+ direccion_trabajo = db.Column(db.String(200))
+ telefono = db.Column(db.String(20))
+ foto_perfil = db.Column(db.String(255))
+ is_active = db.Column(db.Boolean(), unique=False, nullable=False, default=True)
+
+ hijos = db.relationship('Hijo', backref='parent', lazy=True)
def serialize(self):
return {
"id": self.id,
"email": self.email,
- # do not serialize the password, its a security breach
+ "nombre": self.nombre,
+ "apellidos": self.apellidos,
+ "foto_perfil": self.foto_perfil
+ }
+
+class Hijo(db.Model):
+ __tablename__ = 'hijo'
+ id = db.Column(db.Integer, primary_key=True)
+ nombre = db.Column(db.String(80), nullable=False)
+ apellido = db.Column(db.String(80), nullable=False)
+ edad = db.Column(db.Integer, nullable=False)
+ foto_url = db.Column(db.String(255))
+ info_adicional = db.Column(db.Text)
+ intolerancia = db.Column(db.String(100), default="Ninguna")
+ alergia = db.Column(db.String(100), default="Ninguna")
+ asma = db.Column(db.String(50), default="No")
+ tipo_sangre = db.Column(db.String(10), nullable=False)
+ gatea = db.Column(db.String(10), default="No")
+ autonomia_bano = db.Column(db.String(10), default="No")
+ user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
+
+ autorizados = db.relationship('Autorizado', backref='hijo', lazy=True)
+ rutinas = db.relationship('Rutina', secondary=asignacion_rutina, backref=db.backref('hijos_asignados', lazy='dynamic'))
+
+ def serialize(self):
+ return {
+ "id": self.id,
+ "nombre": self.nombre,
+ "apellido": self.apellido,
+ "edad": self.edad,
+ "fotoUrl": self.foto_url,
+ "datosMedicos": {
+ "intolerancia": self.intolerancia,
+ "alergia": self.alergia,
+ "asma": self.asma,
+ "tipoSangre": self.tipo_sangre
+ },
+ "desarrollo": {
+ "gatea": self.gatea,
+ "autonomiaBano": self.autonomia_bano
+ },
+ "info": self.info_adicional
+ }
+
+class Autorizado(db.Model):
+ __tablename__ = 'autorizado'
+ id = db.Column(db.Integer, primary_key=True)
+ nombre = db.Column(db.String(80), nullable=False)
+ apellidos = db.Column(db.String(80), nullable=False)
+ dni = db.Column(db.String(20), nullable=False)
+ telefono = db.Column(db.String(20), nullable=False)
+ parentesco = db.Column(db.String(50))
+ foto_url = db.Column(db.String(255))
+ es_permanente = db.Column(db.Boolean, default=True)
+ valido_desde = db.Column(db.String(20))
+ valido_hasta = db.Column(db.String(20))
+ hijo_id = db.Column(db.Integer, db.ForeignKey('hijo.id'), nullable=False)
+
+ def serialize(self):
+ return {
+ "id": self.id,
+ "nombre": self.nombre,
+ "apellidos": self.apellidos,
+ "dni": self.dni,
+ "telefono": self.telefono,
+ "parentesco": self.parentesco,
+ "fotoUrl": self.foto_url,
+ "esPermanente": self.es_permanente,
+ "validoDesde": self.valido_desde,
+ "validoHasta": self.valido_hasta,
+ "hijoId": self.hijo_id
+ }
+
+class Rutina(db.Model):
+ __tablename__ = 'rutina'
+ id = db.Column(db.Integer, primary_key=True)
+ nombre = db.Column(db.String(120), nullable=False)
+ detalles = db.Column(db.String(250), nullable=True)
+ user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
+
+ actividades = db.relationship('Actividad', backref='rutina', lazy=True, cascade="all, delete-orphan")
+
+ def serialize(self):
+ return {
+ "id": self.id,
+ "nombre": self.nombre,
+ "detalles": self.detalles,
+ "user_id": self.user_id
+ }
+
+class Actividad(db.Model):
+ __tablename__ = 'actividad'
+ id = db.Column(db.Integer, primary_key=True)
+ text = db.Column(db.String(250), nullable=False)
+ time = db.Column(db.String(50), nullable=False)
+ category = db.Column(db.String(50), nullable=False)
+ rutina_id = db.Column(db.Integer, db.ForeignKey('rutina.id'), nullable=False)
+
+ def serialize(self):
+ return {
+ "id": self.id,
+ "text": self.text,
+ "time": self.time,
+ "category": self.category,
+ "rutina_id": self.rutina_id
+ }
+
+class RutinaCompartida(db.Model):
+ __tablename__ = 'rutina_compartida'
+ id = db.Column(db.Integer, primary_key=True)
+ cuidador_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
+ rutina_id = db.Column(db.Integer, db.ForeignKey('rutina.id'), nullable=False)
+ hijo_id = db.Column(db.Integer, db.ForeignKey('hijo.id'), nullable=False)
+
+
+ cuidador = db.relationship('User', foreign_keys=[cuidador_id])
+ rutina_rel = db.relationship('Rutina')
+ hijo_rel = db.relationship('Hijo')
+
+ def serialize(self):
+ return {
+ "id": self.id,
+ "rutina": self.rutina_rel.serialize(),
+ "hijo": self.hijo_rel.serialize(),
+ "cuidador": self.cuidador.serialize()
}
\ No newline at end of file
diff --git a/src/api/routes.py b/src/api/routes.py
index 029589a3a1..85c40b0221 100644
--- a/src/api/routes.py
+++ b/src/api/routes.py
@@ -1,22 +1,340 @@
-"""
-This module takes care of starting the API Server, Loading the DB and Adding the endpoints
-"""
-from flask import Flask, request, jsonify, url_for, Blueprint
-from api.models import db, User
-from api.utils import generate_sitemap, APIException
-from flask_cors import CORS
+from flask import Flask, request, jsonify, Blueprint
+from api.models import db, User, Hijo, Autorizado, Rutina, Actividad, RutinaCompartida
+from flask_jwt_extended import jwt_required, get_jwt_identity, create_access_token
api = Blueprint('api', __name__)
-# Allow CORS requests to this API
-CORS(api)
+@api.route('/login', methods=['POST'])
+def handle_login():
+ body = request.get_json()
+ if not body or "email" not in body or "password" not in body:
+ return jsonify({"msg": "Faltan credenciales"}), 400
+ user = User.query.filter_by(email=body['email'], password=body['password']).first()
+
+ if user is None:
+ return jsonify({"msg": "Correo o contraseña incorrectos"}), 401
+
+ access_token = create_access_token(identity=str(user.id))
+
+ return jsonify({
+ "msg": "Login exitoso",
+ "token": access_token,
+ "user": user.serialize()
+ }), 200
-@api.route('/hello', methods=['POST', 'GET'])
-def handle_hello():
- response_body = {
- "message": "Hello! I'm a message that came from the backend, check the network tab on the google inspector and you will see the GET request"
- }
+@api.route('/registro', methods=['POST'])
+def handle_registro():
+ body = request.get_json()
+ if body is None:
+ return jsonify({"msg": "No se recibió información"}), 400
+
+ user_exists = User.query.filter_by(email=body.get('email')).first()
+ if user_exists:
+ return jsonify({"msg": "El correo electrónico ya está registrado"}), 400
- return jsonify(response_body), 200
+ nuevo_usuario = User(
+ email=body.get('email'),
+ password=body.get('password'),
+ nombre=body.get('nombre'),
+ apellidos=body.get('apellidos'),
+ edad=body.get('edad'),
+ direccion_hogar=body.get('direccionHogar'),
+ direccion_trabajo=body.get('direccionTrabajo'),
+ telefono=body.get('telefonoCompleto'),
+ foto_perfil=body.get('fotoPerfil'),
+ is_active=True
+ )
+
+ db.session.add(nuevo_usuario)
+ try:
+ db.session.commit()
+ return jsonify({"msg": "Usuario creado con éxito"}), 201
+ except Exception as e:
+ db.session.rollback()
+ print(f"Error en servidor: {str(e)}")
+ return jsonify({"msg": "Error interno del servidor", "error": str(e)}), 500
+
+@api.route('/hijos', methods=['POST'])
+@jwt_required()
+def add_hijo():
+ current_user_id = get_jwt_identity()
+ body = request.get_json()
+ datos_medicos = body.get("datosMedicos", {})
+ desarrollo = body.get("desarrollo", {})
+
+ nuevo_hijo = Hijo(
+ nombre=body['nombre'],
+ apellido=body['apellido'],
+ edad=body['edad'],
+ foto_url=body.get('fotoUrl'),
+ info_adicional=body.get('info'),
+ intolerancia=datos_medicos.get('intolerancia'),
+ alergia=datos_medicos.get('alergia'),
+ asma=datos_medicos.get('asma'),
+ tipo_sangre=datos_medicos.get('tipoSangre'),
+ gatea=desarrollo.get('gatea'),
+ autonomia_bano=desarrollo.get('autonomiaBano'),
+ user_id=current_user_id
+ )
+ db.session.add(nuevo_hijo)
+ try:
+ db.session.commit()
+ return jsonify({"msg": "Hijo registrado", "hijo": nuevo_hijo.serialize()}), 201
+ except Exception as e:
+ db.session.rollback()
+ return jsonify({"msg": "Error", "error": str(e)}), 500
+
+@api.route('/hijos/', methods=['DELETE'])
+@jwt_required()
+def delete_hijo(hijo_id):
+ current_user_id = get_jwt_identity()
+ hijo = Hijo.query.filter_by(id=hijo_id, user_id=current_user_id).first()
+
+ if not hijo:
+ return jsonify({"msg": "Hijo no encontrado o no tienes permiso"}), 404
+
+ try:
+ RutinaCompartida.query.filter_by(hijo_id=hijo_id).delete()
+
+ Autorizado.query.filter_by(hijo_id=hijo_id).delete()
+
+ hijo.rutinas = []
+
+ db.session.delete(hijo)
+ db.session.commit()
+ return jsonify({"msg": "Hijo y datos vinculados eliminados con éxito"}), 200
+ except Exception as e:
+ db.session.rollback()
+ return jsonify({"msg": "Error al eliminar", "error": str(e)}), 500
+
+@api.route('/autorizados', methods=['POST'])
+@jwt_required()
+def add_autorizado():
+ body = request.get_json()
+ h_id = body.get('hijoId') or body.get('hijo_id')
+
+ nuevo_auth = Autorizado(
+ nombre=body['nombre'], apellidos=body['apellidos'], dni=body['dni'],
+ telefono=body['telefono'], parentesco=body.get('parentesco'),
+ foto_url=body.get('fotoUrl'), es_permanente=body.get('esPermanente', True),
+ valido_desde=body.get('validoDesde'), valido_hasta=body.get('validoHasta'),
+ hijo_id=h_id
+ )
+ db.session.add(nuevo_auth)
+ try:
+ db.session.commit()
+ return jsonify({"msg": "Autorizado registrado", "autorizado": nuevo_auth.serialize()}), 201
+ except Exception as e:
+ db.session.rollback()
+ return jsonify({"msg": "Error", "error": str(e)}), 500
+
+@api.route('/autorizados/', methods=['DELETE'])
+@jwt_required()
+def delete_autorizado(auth_id):
+ auth = Autorizado.query.get(auth_id)
+ if not auth:
+ return jsonify({"msg": "Autorizado no encontrado"}), 404
+
+ try:
+ db.session.delete(auth)
+ db.session.commit()
+ return jsonify({"msg": "Autorizado eliminado correctamente"}), 200
+ except Exception as e:
+ db.session.rollback()
+ return jsonify({"msg": "Error al eliminar", "error": str(e)}), 500
+
+@api.route('/rutinas', methods=['GET', 'POST'])
+@jwt_required()
+def handle_rutinas():
+ current_user_id = get_jwt_identity()
+ try:
+ if request.method == 'GET':
+ rutinas = Rutina.query.filter_by(user_id=current_user_id).all()
+ return jsonify([r.serialize() for r in rutinas]), 200
+ if request.method == 'POST':
+ body = request.get_json()
+ if not body or not body.get("nombre"):
+ return jsonify({"msg": "El nombre es obligatorio"}), 400
+ nueva_rutina = Rutina(
+ nombre=body.get("nombre"),
+ detalles=body.get("detalles", ""),
+ user_id=current_user_id
+ )
+ db.session.add(nueva_rutina)
+ db.session.commit()
+ return jsonify(nueva_rutina.serialize()), 201
+ except Exception as e:
+ db.session.rollback()
+ return jsonify({"msg": "Error", "error": str(e)}), 500
+
+@api.route('/asignar-rutina', methods=['POST'])
+@jwt_required()
+def asignar_rutina_a_hijos_modal():
+ body = request.get_json()
+ rutina_id = body.get("rutina_id")
+ hijos_ids = body.get("hijo_ids")
+
+ if not rutina_id or not hijos_ids:
+ return jsonify({"msg": "Datos insuficientes"}), 400
+
+ rutina = Rutina.query.get(rutina_id)
+ if not rutina:
+ return jsonify({"msg": "Rutina no encontrada"}), 404
+
+ hijos = Hijo.query.filter(Hijo.id.in_(hijos_ids)).all()
+ rutina.hijos_asignados = hijos
+
+ try:
+ db.session.commit()
+ return jsonify({"msg": "Asignación exitosa"}), 200
+ except Exception as e:
+ db.session.rollback()
+ return jsonify({"msg": "Error", "error": str(e)}), 500
+
+@api.route('/hijos//rutinas', methods=['GET'])
+@jwt_required()
+def get_rutinas_de_hijo(hijo_id):
+ hijo = Hijo.query.get(hijo_id)
+ if not hijo:
+ return jsonify({"msg": "Hijo no encontrado"}), 404
+ return jsonify([r.serialize() for r in hijo.rutinas]), 200
+
+@api.route('/rutinas/', methods=['DELETE'])
+@jwt_required()
+def delete_rutina(rutina_id):
+ current_user_id = get_jwt_identity()
+ rutina = Rutina.query.filter_by(id=rutina_id, user_id=current_user_id).first()
+ if not rutina:
+ return jsonify({"msg": "Rutina no encontrada"}), 404
+ db.session.delete(rutina)
+ db.session.commit()
+ return jsonify({"msg": "Rutina eliminada"}), 200
+
+
+# --- RUTAS DE ACTIVIDADES ---
+
+@api.route('/rutinas//actividades', methods=['GET', 'POST'])
+@jwt_required()
+def handle_actividades(rutina_id):
+ current_user_id = get_jwt_identity()
+ rutina = Rutina.query.filter_by(id=rutina_id, user_id=current_user_id).first()
+ if not rutina:
+ return jsonify({"msg": "Rutina no encontrada"}), 404
+
+ if request.method == 'GET':
+ actividades = Actividad.query.filter_by(rutina_id=rutina_id).all()
+ return jsonify([a.serialize() for a in actividades]), 200
+
+ if request.method == 'POST':
+ try:
+ body = request.get_json()
+ nueva_actividad = Actividad(
+ text=body.get("text"),
+ time=body.get("time"),
+ category=body.get("category"),
+ rutina_id=rutina_id
+ )
+ db.session.add(nueva_actividad)
+ db.session.commit()
+ return jsonify(nueva_actividad.serialize()), 201
+ except Exception as e:
+ db.session.rollback()
+ return jsonify({"msg": "Error al crear actividad", "error": str(e)}), 500
+
+@api.route('/actividades/', methods=['PUT', 'DELETE'])
+@jwt_required()
+def update_delete_actividad(actividad_id):
+ actividad = Actividad.query.get(actividad_id)
+ if not actividad:
+ return jsonify({"msg": "Actividad no encontrada"}), 404
+
+ if request.method == 'PUT':
+ body = request.get_json()
+ actividad.text = body.get("text", actividad.text)
+ actividad.time = body.get("time", actividad.time)
+ actividad.category = body.get("category", actividad.category)
+ db.session.commit()
+ return jsonify(actividad.serialize()), 200
+
+ if request.method == 'DELETE':
+ db.session.delete(actividad)
+ db.session.commit()
+ return jsonify({"msg": "Actividad eliminada"}), 200
+
+
+
+@api.route('/parent-data', methods=['GET'])
+@jwt_required()
+def get_parent_data():
+ current_user_id = get_jwt_identity()
+ user = User.query.get(current_user_id)
+ if not user: return jsonify({"msg": "Usuario no encontrado"}), 404
+
+ hijos = Hijo.query.filter_by(user_id=current_user_id).all()
+ autorizados = db.session.query(Autorizado).join(Hijo).filter(Hijo.user_id == current_user_id).all()
+
+ return jsonify({
+ "hijos": [h.serialize() for h in hijos],
+ "autorizados": [a.serialize() for a in autorizados]
+ }), 200
+
+@api.route('/rutinas/compartir', methods=['POST'])
+@jwt_required()
+def compartir_rutina():
+ body = request.get_json()
+ email_cuidador = body.get("email")
+ rutina_id = body.get("rutina_id")
+ hijo_id = body.get("hijo_id")
+
+ cuidador = User.query.filter_by(email=email_cuidador).first()
+ if not cuidador:
+ return jsonify({"msg": "El correo no pertenece a ningún usuario registrado"}), 404
+
+ existe = RutinaCompartida.query.filter_by(
+ cuidador_id=cuidador.id,
+ rutina_id=rutina_id,
+ hijo_id=hijo_id
+ ).first()
+
+ if existe:
+ return jsonify({"msg": "Esta rutina ya ha sido compartida con este usuario"}), 400
+
+ nueva_asignacion = RutinaCompartida(
+ cuidador_id=cuidador.id,
+ rutina_id=rutina_id,
+ hijo_id=hijo_id
+ )
+
+ try:
+ db.session.add(nueva_asignacion)
+ db.session.commit()
+ return jsonify({"msg": "Rutina compartida con éxito"}), 200
+ except Exception as e:
+ db.session.rollback()
+ return jsonify({"msg": "Error al compartir", "error": str(e)}), 500
+
+@api.route('/cuidador/rutinas', methods=['GET'])
+@jwt_required()
+def get_rutinas_cuidador():
+ current_user_id = get_jwt_identity()
+ asignaciones = RutinaCompartida.query.filter_by(cuidador_id=current_user_id).all()
+ return jsonify([asignacion.serialize() for asignacion in asignaciones]), 200
+
+@api.route('/cuidador/rutinas/', methods=['DELETE'])
+@jwt_required()
+def eliminar_rutina_compartida(asignacion_id):
+ current_user_id = get_jwt_identity()
+ asignacion = RutinaCompartida.query.filter_by(id=asignacion_id, cuidador_id=current_user_id).first()
+
+ if not asignacion:
+ return jsonify({"msg": "No se encontró la asignación o no tienes permiso"}), 404
+
+ try:
+ db.session.delete(asignacion)
+ db.session.commit()
+ return jsonify({"msg": "Ya no tienes acceso a esta rutina"}), 200
+ except Exception as e:
+ db.session.rollback()
+ return jsonify({"msg": "Error al eliminar la asignación", "error": str(e)}), 500
\ No newline at end of file
diff --git a/src/api/utils.py b/src/api/utils.py
index 9c18b4d596..e220dc3fd3 100644
--- a/src/api/utils.py
+++ b/src/api/utils.py
@@ -23,8 +23,6 @@ def has_no_empty_params(rule):
def generate_sitemap(app):
links = ['/admin/']
for rule in app.url_map.iter_rules():
- # Filter out rules we can't navigate to in a browser
- # and rules that require parameters
if "GET" in rule.methods and has_no_empty_params(rule):
url = url_for(rule.endpoint, **(rule.defaults or {}))
if "/admin/" not in url:
diff --git a/src/app.py b/src/app.py
index 1b3340c0fa..be293b7e1c 100644
--- a/src/app.py
+++ b/src/app.py
@@ -1,29 +1,28 @@
-"""
-This module takes care of starting the API Server, Loading the DB and Adding the endpoints
-"""
import os
from flask import Flask, request, jsonify, url_for, send_from_directory
from flask_migrate import Migrate
from flask_swagger import swagger
+from flask_cors import CORS
+from flask_jwt_extended import JWTManager
from api.utils import APIException, generate_sitemap
from api.models import db
from api.routes import api
from api.admin import setup_admin
from api.commands import setup_commands
-# from models import Person
+static_file_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), '../dist/')
-ENV = "development" if os.getenv("FLASK_DEBUG") == "1" else "production"
-static_file_dir = os.path.join(os.path.dirname(
- os.path.realpath(__file__)), '../dist/')
app = Flask(__name__)
app.url_map.strict_slashes = False
-# database condiguration
+app.config["JWT_SECRET_KEY"] = "super-secret-key-change-me"
+jwt = JWTManager(app)
+
+CORS(app)
+
db_url = os.getenv("DATABASE_URL")
if db_url is not None:
- app.config['SQLALCHEMY_DATABASE_URI'] = db_url.replace(
- "postgres://", "postgresql://")
+ app.config['SQLALCHEMY_DATABASE_URI'] = db_url.replace("postgres://", "postgresql://")
else:
app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:////tmp/test.db"
@@ -31,42 +30,28 @@
MIGRATE = Migrate(app, db, compare_type=True)
db.init_app(app)
-# add the admin
setup_admin(app)
-
-# add the admin
setup_commands(app)
-
-# Add all endpoints form the API with a "api" prefix
app.register_blueprint(api, url_prefix='/api')
-# Handle/serialize errors like a JSON object
-
-
@app.errorhandler(APIException)
def handle_invalid_usage(error):
return jsonify(error.to_dict()), error.status_code
-# generate sitemap with all your endpoints
-
+@app.route('/sitemap')
+def sitemap():
+ return generate_sitemap(app)
@app.route('/')
-def sitemap():
- if ENV == "development":
- return generate_sitemap(app)
+def index():
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)):
path = 'index.html'
- response = send_from_directory(static_file_dir, path)
- response.cache_control.max_age = 0 # avoid cache memory
- return response
-
+ return send_from_directory(static_file_dir, path)
-# this only runs if `$ python src/main.py` is executed
if __name__ == '__main__':
PORT = int(os.environ.get('PORT', 3001))
- app.run(host='0.0.0.0', port=PORT, debug=True)
+ app.run(host='0.0.0.0', port=PORT, debug=True)
\ No newline at end of file
diff --git a/src/front/actions.js b/src/front/actions.js
new file mode 100644
index 0000000000..1f66feef31
--- /dev/null
+++ b/src/front/actions.js
@@ -0,0 +1,151 @@
+const backendUrl = import.meta.env.VITE_BACKEND_URL;
+
+export const actions = (store, dispatch) => {
+ return {
+ loadParentData: async () => {
+ const token = localStorage.getItem("token");
+ if (!token) return;
+
+ try {
+
+ const resp = await fetch(`${backendUrl}/api/parent-data`, {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ "Authorization": `Bearer ${token}`
+ }
+ });
+
+ if (!resp.ok) throw new Error("Error cargando datos");
+ const data = await resp.json();
+
+ dispatch({ type: 'set_hijos', payload: data.hijos });
+ dispatch({ type: 'set_autorizados', payload: data.autorizados });
+ } catch (error) {
+ console.error("Error en loadParentData:", error);
+ }
+ },
+
+ addHijo: async (hijoData) => {
+ const token = localStorage.getItem("token");
+ try {
+ const resp = await fetch(`${backendUrl}/api/hijos`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "Authorization": `Bearer ${token}`
+ },
+ body: JSON.stringify(hijoData)
+ });
+ if (resp.ok) {
+ const data = await resp.json();
+ dispatch({ type: 'add_hijo', payload: data.hijo });
+ return true;
+ }
+ return false;
+ } catch (error) {
+ console.error("Error al añadir hijo:", error);
+ return false;
+ }
+ },
+
+ deleteHijo: async (hijoId) => {
+ const token = localStorage.getItem("token");
+ try {
+ const resp = await fetch(`${backendUrl}/api/hijos/${hijoId}`, {
+ method: "DELETE",
+ headers: {
+ "Authorization": `Bearer ${token}`
+ }
+ });
+
+ if (resp.ok) {
+ dispatch({ type: 'delete_hijo', payload: hijoId });
+
+ const respData = await fetch(`${backendUrl}/api/parent-data`, {
+ headers: { "Authorization": `Bearer ${token}` }
+ });
+ if (respData.ok) {
+ const data = await respData.json();
+ dispatch({ type: 'set_autorizados', payload: data.autorizados });
+ }
+ return true;
+ }
+
+ const errorData = await resp.json();
+ console.error("Respuesta del servidor:", errorData.msg);
+ return false;
+ } catch (error) {
+ console.error("Error de red eliminando hijo:", error);
+ return false;
+ }
+ },
+
+ addAutorizado: async (authData) => {
+ const token = localStorage.getItem("token");
+ try {
+ const resp = await fetch(`${backendUrl}/api/autorizados`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "Authorization": `Bearer ${token}`
+ },
+ body: JSON.stringify(authData)
+ });
+ if (resp.ok) {
+ const data = await resp.json();
+ dispatch({ type: 'add_autorizado', payload: data.autorizado });
+ return true;
+ }
+ return false;
+ } catch (error) {
+ console.error("Error al añadir autorizado:", error);
+ return false;
+ }
+ },
+
+ deleteAutorizado: async (authId) => {
+ const token = localStorage.getItem("token");
+ try {
+ const resp = await fetch(`${backendUrl}/api/autorizados/${authId}`, {
+ method: "DELETE",
+ headers: {
+ "Authorization": `Bearer ${token}`
+ }
+ });
+
+ if (resp.ok) {
+ dispatch({ type: 'delete_autorizado', payload: authId });
+ return true;
+ }
+ return false;
+ } catch (error) {
+ console.error("Error eliminando autorizado:", error);
+ return false;
+ }
+ },
+
+ deleteRutinaCompartida: async (asignacionId) => {
+ const token = localStorage.getItem("token");
+ try {
+ const resp = await fetch(`${backendUrl}/api/cuidador/rutinas/${asignacionId}`, {
+ method: "DELETE",
+ headers: {
+ "Authorization": `Bearer ${token}`
+ }
+ });
+
+ if (resp.ok) {
+ return true;
+ }
+
+ const errorData = await resp.json();
+ console.error("Error del servidor:", errorData.msg);
+ return false;
+ } catch (error) {
+ console.error("Error de red eliminando rutina compartida:", error);
+ return false;
+ }
+ }
+ };
+};
\ No newline at end of file
diff --git a/src/front/assets/Icono Baby Zzzync - fondo transparente.png b/src/front/assets/Icono Baby Zzzync - fondo transparente.png
new file mode 100644
index 0000000000..51a789a3a7
Binary files /dev/null and b/src/front/assets/Icono Baby Zzzync - fondo transparente.png differ
diff --git a/src/front/assets/Logo Baby Zzync 1 - vers blanca.png b/src/front/assets/Logo Baby Zzync 1 - vers blanca.png
new file mode 100644
index 0000000000..c95cfec26f
Binary files /dev/null and b/src/front/assets/Logo Baby Zzync 1 - vers blanca.png differ
diff --git a/src/front/assets/Logo Baby Zzync 1.png b/src/front/assets/Logo Baby Zzync 1.png
new file mode 100644
index 0000000000..fee054bdc4
Binary files /dev/null and b/src/front/assets/Logo Baby Zzync 1.png differ
diff --git a/src/front/assets/Logo Baby Zzync 2.png b/src/front/assets/Logo Baby Zzync 2.png
new file mode 100644
index 0000000000..0db0f2de5b
Binary files /dev/null and b/src/front/assets/Logo Baby Zzync 2.png differ
diff --git a/src/front/components/Cardautorizado.jsx b/src/front/components/Cardautorizado.jsx
new file mode 100644
index 0000000000..e181bdbda1
--- /dev/null
+++ b/src/front/components/Cardautorizado.jsx
@@ -0,0 +1,179 @@
+import React from "react";
+import useGlobalReducer from "../hooks/useGlobalReducer";
+import Swal from 'sweetalert2';
+
+export const Cardautorizado = ({ autorizado }) => {
+ const { store, actions } = useGlobalReducer();
+
+ const hijoVinculado = store.hijos?.find(h => h.id === autorizado.hijoId);
+
+ const handleDelete = (e) => {
+ e.stopPropagation();
+
+ Swal.fire({
+ title: '¿Eliminar?',
+ text: `Se borrará la autorización de ${autorizado.nombre} permanentemente.`,
+ icon: 'warning',
+ showCancelButton: true,
+ confirmButtonColor: 'var(--color-descanso)',
+ cancelButtonColor: '#c2c2c2',
+ confirmButtonText: 'Sí, eliminar',
+ cancelButtonText: 'Cancelar',
+ width: '400px',
+ customClass: {
+ popup: 'my-custom-popup',
+ confirmButton: 'rounded-pill px-3 shadow-sm',
+ cancelButton: 'rounded-pill px-3'
+ }
+ }).then(async (result) => {
+ if (result.isConfirmed) {
+
+ const success = await actions.deleteAutorizado(autorizado.id);
+
+ if (success) {
+ Swal.fire({
+ title: 'Eliminado',
+ text: 'Registro de autorizado borrado.',
+ icon: 'success',
+ timer: 1500,
+ showConfirmButton: false,
+ width: '280px',
+ customClass: { popup: 'my-custom-popup' }
+ });
+ } else {
+ Swal.fire({
+ title: 'Error',
+ text: 'No se pudo eliminar el registro del servidor.',
+ icon: 'error'
+ });
+ }
+ }
+ });
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {autorizado.nombre} {autorizado.apellidos}
+
+
+
+
+ Recoge a: {hijoVinculado ? hijoVinculado.nombre : "Hijo no encontrado"}
+
+
+
+ {autorizado.parentesco || "Autorizado"}
+ {autorizado.esPermanente && Permanente }
+
+
+
+ Ver detalles
+
+
+
+
+
+
+
+
+
+
+
+
{autorizado.nombre}
+
{autorizado.apellidos}
+
+
+
+ AUTORIZADO PARA RECOGER A:
+
+ {hijoVinculado ? `${hijoVinculado.nombre} ${hijoVinculado.apellido}` : "Hijo no encontrado"}
+
+
+
+
+ DNI / NIE
+ {autorizado.dni}
+
+
+
+ TELÉFONO
+ {autorizado.telefono}
+
+
+
+ PARENTESCO
+ {autorizado.parentesco || "Autorizado"}
+
+
+
+
Validez del Permiso
+
+ {autorizado.esPermanente ? (
+
+
+ ACCESO PERMANENTE
+
+
+ ) : (
+
+
+ Desde
+ {autorizado.validoDesde}
+
+
+ Hasta
+ {autorizado.validoHasta}
+
+
+ )}
+
+
+
+
+ Cerrar Detalles
+
+
+
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/front/components/Cardhijo.jsx b/src/front/components/Cardhijo.jsx
new file mode 100644
index 0000000000..f8d8ac47d0
--- /dev/null
+++ b/src/front/components/Cardhijo.jsx
@@ -0,0 +1,158 @@
+import React from "react";
+import { useNavigate } from "react-router-dom";
+import useGlobalReducer from "../hooks/useGlobalReducer";
+import Swal from 'sweetalert2';
+
+export const Cardhijo = ({ hijo }) => {
+ const { store, actions } = useGlobalReducer();
+ const navigate = useNavigate();
+
+ const handleDelete = (e) => {
+ e.stopPropagation();
+ Swal.fire({
+ title: '¿Eliminar a ' + hijo.nombre + '?',
+ text: "Esta acción borrará al niño y a sus autorizados de forma permanente.",
+ icon: 'warning',
+ showCancelButton: true,
+ confirmButtonColor: '#ff6b6b',
+ cancelButtonColor: '#c2c2c2',
+ confirmButtonText: 'Sí, eliminar',
+ cancelButtonText: 'Cancelar',
+ width: '400px',
+ padding: '1.5rem',
+ customClass: {
+ popup: 'my-custom-popup',
+ confirmButton: 'rounded-pill px-3 shadow-sm',
+ cancelButton: 'rounded-pill px-3'
+ }
+ }).then(async (result) => {
+ if (result.isConfirmed) {
+ const success = await actions.deleteHijo(hijo.id);
+ if (success) {
+ Swal.fire({
+ title: '¡Eliminado!',
+ text: 'El registro ha sido borrado.',
+ icon: 'success',
+ timer: 1500,
+ showConfirmButton: false,
+ width: '400px',
+ customClass: { popup: 'my-custom-popup' }
+ });
+ } else {
+ Swal.fire({
+ title: 'Error',
+ text: 'No se pudo eliminar el registro del servidor.',
+ icon: 'error',
+ confirmButtonColor: '#4CC9F0'
+ });
+ }
+ }
+ });
+ };
+
+ const tieneIntolerancia = hijo.datosMedicos?.intolerancia && hijo.datosMedicos.intolerancia !== "Ninguna";
+ const tieneAlergia = hijo.datosMedicos?.alergia && hijo.datosMedicos.alergia !== "Ninguna";
+ const tieneAsma = hijo.datosMedicos?.asma && hijo.datosMedicos.asma !== "No";
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
navigate("/asignar-rutina", { state: { hijo } })}
+ style={{
+ width: "75px",
+ height: "75px",
+ borderRadius: "50%",
+ objectFit: "cover",
+ border: "3px solid white",
+ cursor: "pointer"
+ }}
+ />
+
+ {hijo.nombre}
+
+
+
+ {tieneIntolerancia && }
+ {tieneAlergia && }
+ {tieneAsma && }
+
+
+
+
+
+
+
+
+
{hijo.nombre}
+
{hijo.apellido}
+
+
+ {hijo.edad} años
+
+
+
+
Hitos de Desarrollo
+
+
Gatea: {hijo.desarrollo?.gatea || "No"}
+
Baño: {hijo.desarrollo?.autonomiaBano || "No"}
+
+
+
+
Ficha Médica
+
+
Intolerancias: {hijo.datosMedicos?.intolerancia || "Ninguna"}
+
Alergias: {hijo.datosMedicos?.alergia || "Ninguna"}
+
Asma: {hijo.datosMedicos?.asma || "No"}
+
Sangre: {hijo.datosMedicos?.tipoSangre || "No informado"}
+
+
+
+
Notas del padre/madre
+
+ "{hijo.info || "Sin información adicional."}"
+
+
+
+
+ Cerrar
+
+
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/front/components/Carduser.jsx b/src/front/components/Carduser.jsx
new file mode 100644
index 0000000000..c745280b0f
--- /dev/null
+++ b/src/front/components/Carduser.jsx
@@ -0,0 +1,60 @@
+import React, { useState, useEffect } from "react";
+
+export const Carduser = () => {
+ const [userData, setUserData] = useState({
+ nombre: "Usuario Padre",
+ fotoUrl: null
+ });
+
+ useEffect(() => {
+ const storedUser = localStorage.getItem("user");
+
+ if (storedUser) {
+ const parsedUser = JSON.parse(storedUser);
+ setUserData({
+ nombre: parsedUser.nombre || "Usuario Padre",
+ fotoUrl: parsedUser.foto_perfil || null
+ });
+ }
+ }, []);
+
+ return (
+
+
+
+ {userData.fotoUrl ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ {userData.nombre}
+
+
+ @{userData.nombre.toLowerCase().replace(/\s+/g, '')}
+
+
+
+
+ );
+};
+
+const avatarStyle = {
+ width: "50px",
+ height: "50px",
+ borderRadius: "50%",
+ backgroundColor: "var(--color-primario)",
+ overflow: "hidden",
+ flexShrink: 0
+};
+
+const imgStyle = {
+ width: "100%",
+ height: "100%",
+ objectFit: "cover"
+};
\ No newline at end of file
diff --git a/src/front/components/Cloudinary.jsx b/src/front/components/Cloudinary.jsx
new file mode 100644
index 0000000000..56db5c9719
--- /dev/null
+++ b/src/front/components/Cloudinary.jsx
@@ -0,0 +1,93 @@
+import React, { useState, useRef } from 'react';
+
+const Cloudinary = ({ onImageUploaded }) => {
+ const preset_name = "Lmroch";
+ const cloud_name = "dlcubj61o";
+
+ const [image, setImage] = useState('');
+ const [loading, setLoading] = useState(false);
+ const [showMenu, setShowMenu] = useState(false);
+
+ const fileInputRef = useRef(null);
+ const cameraInputRef = useRef(null);
+
+ const uploadImage = async (e) => {
+ const files = e.target.files;
+ if (!files || files.length === 0) return;
+
+ setShowMenu(false);
+ const data = new FormData();
+ data.append('file', files[0]);
+ data.append('upload_preset', preset_name);
+
+ setLoading(true);
+
+ try {
+ const response = await fetch(`https://api.cloudinary.com/v1_1/${cloud_name}/image/upload`, {
+ method: 'POST',
+ body: data
+ });
+
+ const file = await response.json();
+ const urlOptimizada = file.secure_url.replace('/upload/', '/upload/w_150,h_150,c_fill,g_face/');
+ setImage(urlOptimizada);
+
+ if(onImageUploaded) {
+ onImageUploaded(urlOptimizada);
+ }
+
+ setLoading(false);
+ } catch (error) {
+ console.error('Error uploading image:', error);
+ setLoading(false);
+ }
+ };
+
+ return (
+
+
!loading && setShowMenu(!showMenu)}
+ >
+ {!image && !loading &&
+ Foto }
+ {loading &&
Subiendo... }
+ {image && !loading && (
+
+ )}
+
+
+ {showMenu && (
+
+ fileInputRef.current.click()}>
+ Subir archivo
+
+ cameraInputRef.current.click()}>
+ Tomar foto
+
+
+ )}
+
+
+
+
+ );
+}
+
+export default Cloudinary;
\ No newline at end of file
diff --git a/src/front/components/Footer.jsx b/src/front/components/Footer.jsx
index f06302dbd2..4755bbde23 100644
--- a/src/front/components/Footer.jsx
+++ b/src/front/components/Footer.jsx
@@ -1,11 +1,5 @@
export const Footer = () => (
-
+
+ © 2026 Baby Zzzync / Made with love for 4Geeks
+
);
diff --git a/src/front/components/GoogleInput.jsx b/src/front/components/GoogleInput.jsx
new file mode 100644
index 0000000000..6ce91adaf1
--- /dev/null
+++ b/src/front/components/GoogleInput.jsx
@@ -0,0 +1,30 @@
+import React from "react";
+import Autocomplete from "react-google-autocomplete";
+
+const GoogleInput = ({
+ placeholder,
+ icon,
+ onPlaceSelected,
+ onChange,
+ value,
+ required = false
+}) => {
+ return (
+
+ );
+};
+
+export default GoogleInput;
\ No newline at end of file
diff --git a/src/front/components/HeaderApp.jsx b/src/front/components/HeaderApp.jsx
new file mode 100644
index 0000000000..82b661677b
--- /dev/null
+++ b/src/front/components/HeaderApp.jsx
@@ -0,0 +1,71 @@
+import React, { useState } from "react";
+import { useNavigate } from "react-router-dom";
+import logoApp from "../assets/Logo Baby Zzync 1 - vers blanca.png";
+
+export const HeaderApp = ({ showBackButton = false, onBackClick }) => {
+ const [showMenu, setShowMenu] = useState(false);
+ const navigate = useNavigate();
+
+ const handleLogout = () => {
+ localStorage.removeItem("user");
+ localStorage.removeItem("token");
+ navigate("/");
+ };
+
+ const dropdownStyle = {
+ position: "absolute",
+ top: "70px",
+ right: "15px",
+ backgroundColor: "white",
+ borderRadius: "8px",
+ boxShadow: "0px 4px 12px rgba(0,0,0,0.1)",
+ zIndex: 1000,
+ display: showMenu ? "block" : "none",
+ overflow: "hidden"
+ };
+
+ const dropdownItemStyle = {
+ padding: "12px 20px",
+ width: "100%",
+ border: "none",
+ backgroundColor: "transparent",
+ textAlign: "left",
+ fontSize: "0.9rem",
+ fontWeight: "bold",
+ cursor: "pointer",
+ borderBottom: "1px solid #f0f0f0"
+ };
+
+ return (
+
+
+
+ {showBackButton && (
+
+ )}
+
+
+
+
+
setShowMenu(!showMenu)}
+ >
+
+
+ navigate("/rutinas", { state: { from: window.location.pathname } })}>
+ RUTINAS
+
+
+
+ CERRAR SESIÓN
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/front/components/HeaderApp2.jsx b/src/front/components/HeaderApp2.jsx
new file mode 100644
index 0000000000..0f9ba48beb
--- /dev/null
+++ b/src/front/components/HeaderApp2.jsx
@@ -0,0 +1,97 @@
+import React, { useState } from "react";
+import { useNavigate } from "react-router-dom";
+import logoApp from "../assets/Logo Baby Zzync 1 - vers blanca.png";
+
+export const HeaderApp2 = ({ showBackButton = false, onBackClick }) => {
+ const [showMenu, setShowMenu] = useState(false);
+ const navigate = useNavigate();
+
+ const handleLogout = () => {
+ localStorage.removeItem("user");
+ localStorage.removeItem("token");
+ navigate("/");
+ };
+
+ const dropdownStyle = {
+ position: "absolute",
+ top: "70px",
+ right: "15px",
+ backgroundColor: "white",
+ borderRadius: "8px",
+ boxShadow: "0px 4px 12px rgba(0,0,0,0.1)",
+ zIndex: 1000,
+ display: showMenu ? "block" : "none",
+ overflow: "hidden"
+ };
+
+ const dropdownItemStyle = {
+ padding: "12px 20px",
+ width: "100%",
+ border: "none",
+ backgroundColor: "transparent",
+ textAlign: "left",
+ fontSize: "0.9rem",
+ fontWeight: "bold",
+ cursor: "pointer",
+ borderBottom: "1px solid #f0f0f0"
+ };
+
+ return (
+
+
+ {showBackButton ? (
+
+ ) : (
+ navigate("/Home")}
+ >
+ )}
+
+
+
+
+
+
+
+ setShowMenu(!showMenu)}
+ >
+
+
+
+ navigate("/rutinas", { state: { from: window.location.pathname } })}
+ >
+ RUTINAS
+
+
+
+ CERRAR SESIÓN
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/front/components/HeaderApp3.jsx b/src/front/components/HeaderApp3.jsx
new file mode 100644
index 0000000000..50d3089660
--- /dev/null
+++ b/src/front/components/HeaderApp3.jsx
@@ -0,0 +1,112 @@
+import React, { useState } from "react";
+import { useNavigate } from "react-router-dom";
+import logoApp from "../assets/Logo Baby Zzync 1 - vers blanca.png";
+
+export const HeaderApp3 = ({ showBackButton = false, onBackClick }) => {
+ const [showMenu, setShowMenu] = useState(false);
+ const navigate = useNavigate();
+
+ const handleLogout = () => {
+ localStorage.removeItem("user");
+ localStorage.removeItem("token");
+ navigate("/");
+ };
+
+ const handleBack = () => {
+ if (onBackClick) {
+ onBackClick();
+ } else {
+ navigate(-1);
+ }
+ };
+
+ const dropdownStyle = {
+ position: "absolute",
+ top: "70px",
+ right: "15px",
+ backgroundColor: "white",
+ borderRadius: "8px",
+ boxShadow: "0px 4px 12px rgba(0,0,0,0.1)",
+ zIndex: 1000,
+ display: showMenu ? "block" : "none",
+ overflow: "hidden"
+ };
+
+ const dropdownItemStyle = {
+ padding: "12px 20px",
+ width: "100%",
+ border: "none",
+ backgroundColor: "transparent",
+ textAlign: "left",
+ fontSize: "0.9rem",
+ fontWeight: "bold",
+ cursor: "pointer",
+ borderBottom: "1px solid #f0f0f0"
+ };
+
+ return (
+
+
+ {showBackButton && (
+
+ )}
+
+
+
+
+
+
+
+ setShowMenu(!showMenu)}
+ >
+
+
+
+ {
+ setShowMenu(false);
+ navigate("/home");
+ }}
+ >
+ HOME
+
+
+ {
+ setShowMenu(false);
+ navigate("/rutinas", { state: { from: window.location.pathname } });
+ }}
+ >
+ RUTINAS
+
+
+
+ CERRAR SESIÓN
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/front/components/HeaderApp4.jsx b/src/front/components/HeaderApp4.jsx
new file mode 100644
index 0000000000..ecebc95302
--- /dev/null
+++ b/src/front/components/HeaderApp4.jsx
@@ -0,0 +1,95 @@
+import React, { useState } from "react";
+import { useNavigate } from "react-router-dom";
+import logoApp from "../assets/Logo Baby Zzync 1 - vers blanca.png";
+
+export const HeaderApp4 = ({ showBackButton = false, onBackClick }) => {
+ const [showMenu, setShowMenu] = useState(false);
+ const navigate = useNavigate();
+
+ const handleLogout = () => {
+ localStorage.removeItem("user");
+ localStorage.removeItem("token");
+ navigate("/");
+ };
+
+ const dropdownStyle = {
+ position: "absolute",
+ top: "70px",
+ right: "15px",
+ backgroundColor: "white",
+ borderRadius: "8px",
+ boxShadow: "0px 4px 12px rgba(0,0,0,0.1)",
+ zIndex: 1000,
+ display: showMenu ? "block" : "none",
+ overflow: "hidden"
+ };
+
+ const dropdownItemStyle = {
+ padding: "12px 20px",
+ width: "100%",
+ border: "none",
+ backgroundColor: "transparent",
+ textAlign: "left",
+ fontSize: "0.9rem",
+ fontWeight: "bold",
+ cursor: "pointer",
+ borderBottom: "1px solid #f0f0f0"
+ };
+
+ return (
+
+
+ {showBackButton && (
+
+ )}
+
+
+
+
+
+
+
+ setShowMenu(!showMenu)}
+ >
+
+
+
+ {
+ setShowMenu(false);
+ navigate("/home");
+ }}
+ >
+ HOME
+
+
+
+ CERRAR SESIÓN
+
+
+
+ );
+};
\ No newline at end of file
diff --git "a/src/front/components/InfoNi\303\261o.jsx" "b/src/front/components/InfoNi\303\261o.jsx"
new file mode 100644
index 0000000000..948c15499c
--- /dev/null
+++ "b/src/front/components/InfoNi\303\261o.jsx"
@@ -0,0 +1,48 @@
+import React from "react";
+
+export const InfoNiño = ({ nombre, apellidos, edad, fotoUrl }) => {
+ return (
+
+
+
+
+
NOMBRE:
+
+ {nombre}
+
+
+
+
APELLIDOS:
+
+ {apellidos}
+
+
+
+
EDAD:
+
+ {edad}
+
+
+
+
+
+
+ {fotoUrl ? (
+
+ ) : (
+
+ FOTO NIÑO
+
+ )}
+
+
+
+ );
+};
\ 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..368ee63e11
--- /dev/null
+++ b/src/front/components/ProtectedRoute.jsx
@@ -0,0 +1,44 @@
+import React from "react";
+import { useNavigate, Navigate } from "react-router-dom";
+
+const ProtectedRoute = ({ children }) => {
+ const navigate = useNavigate();
+ const user = localStorage.getItem("user");
+
+ if (!user || user === "undefined" || user === "null") {
+ return (
+ <>
+
+ {children}
+
+
+
+
+
+
Acceso Restringido
+
Debes iniciar sesión para gestionar tus rutinas.
+
+
navigate("/")}
+ className="btn w-100 py-3 fw-bold"
+ style={{ backgroundColor: "#007bff", color: "white", borderRadius: "50px", border: "none" }}
+ >
+ Ir al Login
+
+
+
+ >
+ );
+ }
+
+ return children;
+};
+
+export default ProtectedRoute;
\ No newline at end of file
diff --git a/src/front/hooks/useGlobalReducer.jsx b/src/front/hooks/useGlobalReducer.jsx
index 6aeb9d768e..65bf22534b 100644
--- a/src/front/hooks/useGlobalReducer.jsx
+++ b/src/front/hooks/useGlobalReducer.jsx
@@ -1,6 +1,7 @@
// Import necessary hooks and functions from React.
import { useContext, useReducer, createContext } from "react";
import storeReducer, { initialStore } from "../store" // Import the reducer and the initial state.
+import { actions } from "../actions";
// Create a context to hold the global state of the application
// We will call this global state the "store" to avoid confusion while using local states
@@ -11,14 +12,18 @@ const StoreContext = createContext()
export function StoreProvider({ children }) {
// Initialize reducer with the initial state.
const [store, dispatch] = useReducer(storeReducer, initialStore())
+
+
+ const storeActions = actions(store, dispatch);
+
// Provide the store and dispatch method to all child components.
- return
+ return
{children}
}
// Custom hook to access the global state and dispatch function.
export default function useGlobalReducer() {
- const { dispatch, store } = useContext(StoreContext)
- return { dispatch, store };
+ const { dispatch, store, actions } = useContext(StoreContext)
+ return { dispatch, store, actions };
}
\ No newline at end of file
diff --git a/src/front/index.css b/src/front/index.css
index e69de29bb2..f730651298 100644
--- a/src/front/index.css
+++ b/src/front/index.css
@@ -0,0 +1,132 @@
+:root {
+ --color-primario: #480CA8;
+ --color-secundario: #7209B7;
+ --color-alimentacion: #480CA8;
+ --color-descanso: #4CC9F0;
+ --color-cambio-pañal: #D65445;
+ --color-medicacion: #EDD03E;
+ --color-tiempo-de-ejercicio: #57D23B;
+ --color-fondo: #c9c9c9;
+ --color-fondoBotones: #bce9f5;
+}
+
+.bg-registro {
+ background-color: #1a1a1a;
+ min-height: 100vh;
+ width: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 20px;
+ font-family: 'Montserrat', sans-serif;
+ box-sizing: border-box;
+}
+
+.phone-frame {
+ width: 460px;
+ height: 809px;
+ background-color: #000;
+ border-radius: 60px;
+ padding: 12px;
+ box-shadow: 0 25px 50px rgba(0, 0, 0, 0.5);
+ display: flex;
+ box-sizing: border-box;
+ position: relative;
+ border: 4px solid #333;
+}
+
+.mobile-container {
+ flex: 1;
+ height: 100%;
+ background-color: white;
+ border-radius: 45px;
+ overflow-y: auto;
+ overflow-x: hidden;
+ display: flex;
+ flex-direction: column;
+ position: relative;
+ box-sizing: border-box;
+}
+
+.mobile-container::-webkit-scrollbar {
+ width: 4px;
+}
+.mobile-container::-webkit-scrollbar-thumb {
+ background: var(--color-fondoBotones);
+ border-radius: 10px;
+}
+
+.input-line {
+ border: none !important;
+ border-bottom: 1px solid #ccc !important;
+ border-radius: 0 !important;
+ box-shadow: none !important;
+ background-color: transparent !important;
+}
+
+.profile-wrapper {
+ position: relative;
+ width: 100px;
+ margin: 0 auto;
+}
+
+.profile-circle {
+ width: 100px;
+ height: 100px;
+ border: 2px dashed var(--color-primario);
+ border-radius: 50%;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ color: var(--color-primario);
+ overflow: hidden;
+ padding: 0;
+ cursor: pointer;
+ background-color: transparent;
+}
+
+.photo-menu {
+ position: absolute;
+ top: 105px;
+ left: 50%;
+ transform: translateX(-50%);
+ background-color: white;
+ border-radius: 10px;
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
+ display: flex;
+ flex-direction: column;
+ z-index: 100;
+ width: 140px;
+ overflow: hidden;
+}
+
+.photo-menu button {
+ background: none;
+ border: none;
+ padding: 10px;
+ text-align: left;
+ font-size: 14px;
+ color: var(--color-primario);
+ cursor: pointer;
+ transition: background 0.2s;
+}
+
+.photo-menu button:hover {
+ background-color: var(--color-fondoBotones);
+}
+
+.no-spinners::-webkit-outer-spin-button,
+.no-spinners::-webkit-inner-spin-button {
+ -webkit-appearance: none;
+ margin: 0;
+}
+
+.no-spinners[type=number] {
+ -moz-appearance: textfield;
+}
+
+.my-custom-popup {
+ border-radius: 30px !important;
+ font-family: 'Montserrat', sans-serif;
+}
\ No newline at end of file
diff --git a/src/front/pages/AddAutorizado.jsx b/src/front/pages/AddAutorizado.jsx
new file mode 100644
index 0000000000..ce7994b5f6
--- /dev/null
+++ b/src/front/pages/AddAutorizado.jsx
@@ -0,0 +1,196 @@
+import React, { useState } from "react";
+import { useNavigate, Link } from "react-router-dom";
+import useGlobalReducer from "../hooks/useGlobalReducer";
+import logoApp from "../assets/Logo Baby Zzync 1 - vers blanca.png";
+import Cloudinary from "../components/Cloudinary.jsx";
+import Swal from 'sweetalert2';
+
+export const AddAutorizado = () => {
+ const { store, actions } = useGlobalReducer();
+ const navigate = useNavigate();
+
+ const [nombre, setNombre] = useState("");
+ const [apellidos, setApellidos] = useState("");
+ const [telefono, setTelefono] = useState("");
+ const [dni, setDni] = useState("");
+ const [direccion, setDireccion] = useState("");
+ const [parentesco, setParentesco] = useState("");
+ const [hijoId, setHijoId] = useState("");
+ const [foto, setFoto] = useState(null);
+
+ const [fechaInicio, setFechaInicio] = useState("");
+ const [fechaFin, setFechaFin] = useState("");
+ const [esPermanente, setEsPermanente] = useState(false);
+
+ const handleTelefonoChange = (e) => {
+ const value = e.target.value;
+ const onlyNums = value.replace(/[^0-9]/g, '');
+ setTelefono(onlyNums);
+ };
+
+ const handleSave = async () => {
+ const fechasValidas = esPermanente ? true : (fechaInicio && fechaFin);
+
+ if (!nombre || !apellidos || !telefono || !dni || !parentesco || !hijoId || !fechasValidas) {
+ Swal.fire({
+ title: '¡Faltan datos!',
+ text: 'Rellena todos los campos, incluyendo a quién autorizas recoger.',
+ icon: 'warning',
+ confirmButtonColor: 'var(--color-primario)',
+ customClass: { popup: 'my-custom-popup' }
+ });
+ return;
+ }
+
+ const nuevoAutorizado = {
+ nombre,
+ apellidos,
+ telefono,
+ dni,
+ direccion,
+ parentesco,
+ hijoId: parseInt(hijoId),
+ fotoUrl: foto,
+ esPermanente,
+ validoDesde: esPermanente ? "Permanente" : fechaInicio,
+ validoHasta: esPermanente ? "Indefinido" : fechaFin
+ };
+
+
+ const success = await actions.addAutorizado(nuevoAutorizado);
+
+ if (success) {
+ Swal.fire({
+ position: 'center',
+ icon: 'success',
+ title: '¡Autorizado Guardado!',
+ text: 'Los datos se han guardado correctamente.',
+ showConfirmButton: false,
+ timer: 1500,
+ customClass: { popup: 'my-custom-popup' }
+ }).then(() => {
+ navigate("/menupadre");
+ });
+ } else {
+ Swal.fire({
+ title: 'Error',
+ text: 'No se pudo conectar con el servidor para guardar.',
+ icon: 'error'
+ });
+ }
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/front/pages/Addhijo.jsx b/src/front/pages/Addhijo.jsx
new file mode 100644
index 0000000000..fa1e18a8c2
--- /dev/null
+++ b/src/front/pages/Addhijo.jsx
@@ -0,0 +1,252 @@
+import React, { useState } from "react";
+import { useNavigate, Link } from "react-router-dom";
+import useGlobalReducer from "../hooks/useGlobalReducer";
+import Cloudinary from "../components/Cloudinary.jsx";
+import logoApp from "../assets/Logo Baby Zzync 1 - vers blanca.png";
+import Swal from 'sweetalert2';
+
+export const Addhijo = () => {
+ const { store, actions } = useGlobalReducer();
+ const navigate = useNavigate();
+
+ const [nombre, setNombre] = useState("");
+ const [apellidos, setApellidos] = useState("");
+ const [edad, setEdad] = useState(0);
+ const [info, setInfo] = useState("");
+ const [foto, setFoto] = useState("");
+
+ const [hasIntolerancia, setHasIntolerancia] = useState(false);
+ const [hasAlergia, setHasAlergia] = useState(false);
+ const [hasAsma, setHasAsma] = useState(false);
+
+ const [intolerancia, setIntolerancia] = useState("");
+ const [alergia, setAlergia] = useState("");
+ const [nivelAsma, setNivelAsma] = useState("");
+ const [tipoSangre, setTipoSangre] = useState("");
+
+ const [gatea, setGatea] = useState(false);
+ const [vaAlBano, setVaAlBano] = useState(false);
+
+ const handleSave = async () => {
+ if (!nombre || !apellidos || edad === 0) {
+ Swal.fire({
+ title: '¡Ups!',
+ text: 'Por favor, rellena el nombre, apellidos y selecciona una edad.',
+ icon: 'warning',
+ confirmButtonText: 'Corregir',
+ confirmButtonColor: 'var(--color-primario)',
+ width: '400px',
+ customClass: {
+ popup: 'my-custom-popup',
+ confirmButton: 'rounded-pill px-4 shadow-sm'
+ }
+ });
+ return;
+ }
+
+ const localUserData = JSON.parse(localStorage.getItem("user"));
+
+ const hijoData = {
+ nombre,
+ apellido: apellidos,
+ edad,
+ info,
+ fotoUrl: foto,
+ user_id: localUserData?.id,
+ desarrollo: {
+ gatea: gatea ? "Sí" : "No",
+ autonomiaBano: vaAlBano ? "Sí" : "No"
+ },
+ datosMedicos: {
+ intolerancia: hasIntolerancia ? intolerancia : "Ninguna",
+ alergia: hasAlergia ? alergia : "Ninguna",
+ asma: hasAsma ? nivelAsma : "No",
+ tipoSangre: tipoSangre || "No informado"
+ }
+ };
+
+ const success = await actions.addHijo(hijoData);
+
+ if (success) {
+ Swal.fire({
+ icon: 'success',
+ title: '¡Niño registrado!',
+ showConfirmButton: false,
+ timer: 1500,
+ width: '400px',
+ customClass: { popup: 'my-custom-popup' }
+ }).then(() => {
+ navigate("/menupadre");
+ });
+ } else {
+ Swal.fire({
+ icon: 'error',
+ title: 'Error',
+ text: 'No se pudo guardar la información en el servidor.',
+ confirmButtonColor: 'var(--color-primario)',
+ });
+ }
+ };
+
+ const selectStyle = { fontSize: "0.85rem", backgroundColor: "#f8f9fa" };
+
+ return (
+
+
+
+ {/* Header */}
+
+
+
+
+
+
+
+
+
setFoto(url)} />
+
+
+
setNombre(e.target.value)}
+ />
+
setApellidos(e.target.value)}
+ />
+
+
Edad de tu peque
+
+ setEdad(Math.max(0, edad - 1))}>
+
+
+
+ setEdad(edad + 1)}>
+
+
+
+
+
+
Información Médica Especial
+
+
+
+
Intolerancias
+
+ setHasIntolerancia(e.target.checked)} />
+
+
+ {hasIntolerancia && (
+
setIntolerancia(e.target.value)}>
+ ¿Cuál?
+ Lactosa
+ Gluten
+ Fructosa
+ Otra
+
+ )}
+
+
+
+
+
Alergias
+
+ setHasAlergia(e.target.checked)} />
+
+
+ {hasAlergia && (
+
setAlergia(e.target.value)}>
+ ¿A qué?
+ Frutos Secos
+ Pescado
+ Huevo
+ Polen
+ Medicamentos
+
+ )}
+
+
+
+
+
¿Asmático/a?
+
+ setHasAsma(e.target.checked)} />
+
+
+ {hasAsma && (
+
setNivelAsma(e.target.value)}>
+ Frecuencia/Nivel
+ Ocasional
+ Moderado
+ Crónico
+
+ )}
+
+
+
+ Grupo Sanguíneo
+ setTipoSangre(e.target.value)}
+ >
+ Selecciona grupo...
+ A+ A-
+ B+ B-
+ O+ O-
+ AB+ AB-
+
+
+
+
+
+
Hitos de Desarrollo
+
+
+
¿Ya gatea?
+
+ setGatea(e.target.checked)} />
+
+
+
+
+
¿Va al baño solo/a?
+
+ setVaAlBano(e.target.checked)} />
+
+
+
+
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/front/pages/AsignarRutina.jsx b/src/front/pages/AsignarRutina.jsx
new file mode 100644
index 0000000000..a0a718b889
--- /dev/null
+++ b/src/front/pages/AsignarRutina.jsx
@@ -0,0 +1,69 @@
+import React, { useState, useEffect } from "react";
+import { useNavigate, useLocation } from "react-router-dom";
+import { HeaderApp4 } from "../components/HeaderApp4";
+
+export const AsignarRutina = () => {
+ const navigate = useNavigate();
+ const location = useLocation();
+ const [rutinasAsignadas, setRutinasAsignadas] = useState([]);
+ const hijo = location.state?.hijo;
+ const apiUrl = import.meta.env.VITE_BACKEND_URL || "http://localhost:3001";
+
+ useEffect(() => {
+ if (hijo) {
+ const token = localStorage.getItem("token");
+ fetch(`${apiUrl}/api/hijos/${hijo.id}/rutinas`, {
+ headers: { "Authorization": `Bearer ${token}` }
+ })
+ .then(res => res.json())
+ .then(data => {
+ if (Array.isArray(data)) setRutinasAsignadas(data);
+ })
+ .catch(err => console.error(err));
+ }
+ }, [hijo, apiUrl]);
+
+ return (
+
+
navigate("/menupadre")} />
+
+
+
+
+ Rutinas de {hijo?.nombre}
+
+
+
+ {rutinasAsignadas.length === 0 ? (
+
+
+
No hay rutinas asignadas
+
navigate("/rutinas", { state: { hijo } })}>IR A ASIGNAR
+
+ ) : (
+ rutinasAsignadas.map(rutina => (
+
navigate(`/detalle-rutina-hijo/${rutina.id}`, { state: { rutina, hijo } })}
+ >
+
+
+
{rutina.nombre}
+ {rutina.detalles}
+
+
+
+
+ ))
+ )}
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/front/pages/CrearRutina.jsx b/src/front/pages/CrearRutina.jsx
new file mode 100644
index 0000000000..2ea9d9d289
--- /dev/null
+++ b/src/front/pages/CrearRutina.jsx
@@ -0,0 +1,133 @@
+import React, { useState } from "react";
+import { useNavigate } from "react-router-dom";
+import { HeaderApp4 } from "../components/HeaderApp4";
+
+export const CrearRutina = () => {
+ const navigate = useNavigate();
+
+ const [nombre, setNombre] = useState("");
+ const [detalles, setDetalles] = useState("");
+ const [errores, setErrores] = useState({ nombre: false, detalles: false });
+
+ const apiUrl = import.meta.env.VITE_BACKEND_URL || "http://localhost:3001";
+
+ const handleCrearRutina = async () => {
+ const token = localStorage.getItem("token");
+
+ if (!token) {
+ navigate("/");
+ return;
+ }
+
+ const nuevosErrores = {
+ nombre: nombre.trim() === "",
+ detalles: detalles.trim() === ""
+ };
+ setErrores(nuevosErrores);
+
+ if (!nuevosErrores.nombre && !nuevosErrores.detalles) {
+ try {
+ const response = await fetch(`${apiUrl}/api/rutinas`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "Authorization": `Bearer ${token}`
+ },
+ body: JSON.stringify({ nombre, detalles })
+ });
+
+ if (response.ok) {
+ navigate("/rutinas", { replace: true });
+ }
+ } catch (error) {
+ console.error("Error en la petición:", error);
+ }
+ }
+ };
+
+ return (
+
+
navigate("/rutinas")} />
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/front/pages/DetalleRutinaHijo.jsx b/src/front/pages/DetalleRutinaHijo.jsx
new file mode 100644
index 0000000000..5691ac3470
--- /dev/null
+++ b/src/front/pages/DetalleRutinaHijo.jsx
@@ -0,0 +1,159 @@
+import React, { useState, useEffect } from "react";
+import { useNavigate, useParams, useLocation } from "react-router-dom";
+import { HeaderApp4 } from "../components/HeaderApp4";
+import Swal from 'sweetalert2';
+
+const getCategoryIcon = (category) => {
+ switch (category) {
+ case 'milk': return ;
+ case 'sleep': return ;
+ case 'diaper': return ;
+ case 'medicine': return ;
+ case 'exercise': return ;
+ default: return ;
+ }
+};
+
+const getCategoryColor = (category) => {
+ switch (category) {
+ case 'milk': return '#4FC3F7';
+ case 'sleep': return '#7986CB';
+ case 'diaper': return '#FFB74D';
+ case 'medicine': return '#E57373';
+ case 'exercise': return '#81C784';
+ default: return 'var(--color-primario)';
+ }
+};
+
+export const DetalleRutinaHijo = () => {
+ const { id } = useParams();
+ const navigate = useNavigate();
+ const location = useLocation();
+ const [actividades, setActividades] = useState([]);
+
+
+ const { rutina, hijo, esCuidador } = location.state || {};
+ const apiUrl = import.meta.env.VITE_BACKEND_URL || "http://localhost:3001";
+
+ useEffect(() => {
+ const token = localStorage.getItem("token");
+ fetch(`${apiUrl}/api/rutinas/${id}/actividades`, {
+ headers: { "Authorization": `Bearer ${token}` }
+ })
+ .then(res => res.json())
+ .then(data => {
+ if (Array.isArray(data)) {
+ setActividades(data.sort((a, b) => a.time.localeCompare(b.time)));
+ }
+ });
+ }, [id, apiUrl]);
+
+ const handleSendRoutine = async () => {
+ const { value: email } = await Swal.fire({
+ title: 'Compartir Rutina',
+ text: `Introduce el email del cuidador para compartir la rutina de ${hijo?.nombre}`,
+ input: 'email',
+ inputPlaceholder: 'email@ejemplo.com',
+ showCancelButton: true,
+ confirmButtonText: 'Enviar',
+ cancelButtonText: 'Cancelar',
+ confirmButtonColor: 'var(--color-primario)',
+ cancelButtonColor: '#aaa',
+ customClass: {
+ popup: 'rounded-4 shadow',
+ confirmButton: 'rounded-pill px-4',
+ cancelButton: 'rounded-pill px-4'
+ }
+ });
+
+ if (email) {
+ try {
+ const token = localStorage.getItem("token");
+ const response = await fetch(`${apiUrl}/api/rutinas/compartir`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${token}`
+ },
+ body: JSON.stringify({
+ email: email,
+ rutina_id: id,
+ hijo_id: hijo?.id
+ })
+ });
+
+ if (response.ok) {
+ Swal.fire({
+ icon: 'success',
+ title: '¡Rutina compartida!',
+ text: `Se ha enviado la información a ${email}`,
+ timer: 2000,
+ showConfirmButton: false
+ });
+ } else {
+ const errorData = await response.json();
+ throw new Error(errorData.msg || 'No se pudo compartir');
+ }
+ } catch (error) {
+ Swal.fire({
+ icon: 'error',
+ title: 'Hubo un problema',
+ text: error.message,
+ confirmButtonColor: 'var(--color-primario)'
+ });
+ }
+ }
+ };
+
+ return (
+
+
navigate(-1)}
+ />
+
+
+
+
{rutina?.nombre}
+
+ Seguimiento para: {hijo?.nombre}
+
+
+
+
+ {actividades.length === 0 ? (
+
No hay tareas en esta rutina.
+ ) : (
+ actividades.map((item) => (
+
+
+
+ {getCategoryIcon(item.category)}
+
+
+
{item.text}
+ {item.time} hs - {item.category.toUpperCase()}
+
+
+
+ ))
+ )}
+
+
+ {!esCuidador && (
+
+
+
+ ENVIAR RUTINA
+
+
+ )}
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/front/pages/FamiliaRutina.jsx b/src/front/pages/FamiliaRutina.jsx
new file mode 100644
index 0000000000..dc1af696d3
--- /dev/null
+++ b/src/front/pages/FamiliaRutina.jsx
@@ -0,0 +1,224 @@
+import React, { useState, useEffect } from "react";
+import { useNavigate, useParams } from "react-router-dom";
+import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+import { StaticTimePicker } from '@mui/x-date-pickers/StaticTimePicker';
+import { Dialog, DialogContent, DialogActions, DialogTitle, Button, Typography, TextField, MenuItem, Snackbar, Alert } from '@mui/material';
+import dayjs from 'dayjs';
+import 'dayjs/locale/es';
+import { HeaderApp4 } from "../components/HeaderApp4";
+
+const getCategoryIcon = (category) => {
+ switch (category) {
+ case 'milk': return ;
+ case 'sleep': return ;
+ case 'diaper': return ;
+ case 'medicine': return ;
+ case 'exercise': return ;
+ default: return ;
+ }
+};
+
+const getCategoryColor = (category) => {
+ switch (category) {
+ case 'milk': return '#4FC3F7';
+ case 'sleep': return '#7986CB';
+ case 'diaper': return '#FFB74D';
+ case 'medicine': return '#E57373';
+ case 'exercise': return '#81C784';
+ default: return 'var(--color-primario)';
+ }
+};
+
+export const FamiliaRutina = () => {
+ const navigate = useNavigate();
+ const { id } = useParams();
+ const apiUrl = import.meta.env.VITE_BACKEND_URL || "http://localhost:3001";
+
+ const [task, setTask] = useState("");
+ const [time, setTime] = useState(dayjs());
+ const [category, setCategory] = useState("");
+ const [list, setList] = useState([]);
+ const [snackbar, setSnackbar] = useState({ open: false, message: "", severity: "success" });
+ const [openTimeModal, setOpenTimeModal] = useState(false);
+ const [openEditModal, setOpenEditModal] = useState(false);
+ const [editingItem, setEditingItem] = useState(null);
+ const [errors, setErrors] = useState({ task: false, category: false });
+
+ useEffect(() => {
+ const token = localStorage.getItem("token");
+ if (id && token) {
+ fetch(`${apiUrl}/api/rutinas/${id}/actividades`, {
+ headers: { "Authorization": `Bearer ${token}` }
+ })
+ .then(res => res.json())
+ .then(data => {
+ if (Array.isArray(data)) {
+ setList(data.sort((a, b) => a.time.localeCompare(b.time)));
+ }
+ })
+ .catch(err => console.error(err));
+ }
+ }, [id, apiUrl]);
+
+ const handleAddTask = () => {
+ const newTaskError = task.trim() === "";
+ const newCategoryError = category === "";
+ setErrors({ task: newTaskError, category: newCategoryError });
+
+ if (!newTaskError && !newCategoryError) {
+ const token = localStorage.getItem("token");
+ const nuevaActividad = {
+ text: task,
+ time: time.format("HH:mm"),
+ category: category,
+ };
+
+ fetch(`${apiUrl}/api/rutinas/${id}/actividades`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "Authorization": `Bearer ${token}`
+ },
+ body: JSON.stringify(nuevaActividad)
+ })
+ .then(res => res.json())
+ .then(data => {
+ setList([...list, data].sort((a, b) => a.time.localeCompare(b.time)));
+ setTask("");
+ setCategory("");
+ setSnackbar({ open: true, message: "Actividad añadida", severity: "success" });
+ });
+ }
+ };
+
+ const handleOpenEdit = (item) => {
+ setEditingItem({ ...item, time: dayjs(`2024-01-01 ${item.time}`) });
+ setOpenEditModal(true);
+ };
+
+ const handleUpdateTask = () => {
+ const token = localStorage.getItem("token");
+ const updatedData = {
+ text: editingItem.text,
+ time: editingItem.time.format("HH:mm"),
+ category: editingItem.category
+ };
+
+ fetch(`${apiUrl}/api/actividades/${editingItem.id}`, {
+ method: "PUT",
+ headers: {
+ "Content-Type": "application/json",
+ "Authorization": `Bearer ${token}`
+ },
+ body: JSON.stringify(updatedData)
+ })
+ .then(res => res.json())
+ .then(data => {
+ setList(list.map(item => item.id === editingItem.id ? data : item).sort((a, b) => a.time.localeCompare(b.time)));
+ setOpenEditModal(false);
+ setSnackbar({ open: true, message: "Actualizado", severity: "info" });
+ });
+ };
+
+ return (
+
+
navigate("/rutinas", { replace: true })} />
+
+
+
+ setTask(e.target.value)}
+ />
+
+ setOpenTimeModal(true)}>
+ Hora: {time.format("HH:mm")}
+
+
+
+ setCategory(e.target.value)}>
+ CATEGORÍA
+ ALIMENTACIÓN
+ DESCANSO
+ CAMBIO DE PAÑAL
+ MEDICINA
+ EJERCICIO
+
+
+
+ AÑADIR ACTIVIDAD
+
+
+
+
+ {list.length === 0 ? (
+
+
+
Sin actividades aún
+
+ ) : (
+ list.map((item) => (
+
+
+
+ {getCategoryIcon(item.category)}
+
+
+
{item.text}
+ {item.time}
+
+
handleOpenEdit(item)}>
+ EDITAR
+
+
+
+ ))
+ )}
+
+
+
+ setOpenEditModal(false)} fullWidth maxWidth="xs">
+ Editar Actividad
+
+ setEditingItem({ ...editingItem, text: e.target.value })} />
+
+ setEditingItem({ ...editingItem, time: val })}
+ slotProps={{ actionBar: { actions: [] } }}
+ />
+
+
+
+ setOpenEditModal(false)}>Cerrar
+ Guardar
+
+
+
+ setOpenTimeModal(false)}>
+
+
+ setTime(val)}
+ slotProps={{ actionBar: { actions: [] } }}
+ />
+
+
+
+ setOpenTimeModal(false)} style={{ backgroundColor: "var(--color-primario)", borderRadius: "50px" }}>OK
+
+
+
+ setSnackbar({ ...snackbar, open: false })}>
+ {snackbar.message}
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/front/pages/Home.jsx b/src/front/pages/Home.jsx
index 341ed21768..955eccea86 100644
--- a/src/front/pages/Home.jsx
+++ b/src/front/pages/Home.jsx
@@ -1,52 +1,51 @@
-import React, { useEffect } from "react"
-import rigoImageUrl from "../assets/img/rigo-baby.jpg";
-import useGlobalReducer from "../hooks/useGlobalReducer.jsx";
+import React from "react";
+import { Link } from "react-router-dom";
+import { HeaderApp } from "../components/HeaderApp";
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 (
-
-
Hello Rigo!!
-
-
-
-
- {store.message ? (
- {store.message}
- ) : (
-
- Loading message from the backend (make sure your python 🐍 backend is running)...
-
- )}
-
-
- );
-};
\ No newline at end of file
+ const btnStyle = {
+ backgroundColor: "var(--color-fondoBotones)",
+ borderRadius: "15px",
+ display: "flex",
+ flexDirection: "column",
+ alignItems: "center",
+ justifyContent: "center",
+ border: "none",
+ padding: "1.5rem",
+ transition: "all 0.2s ease"
+ };
+
+ const iconStyle = { fontSize: "1.5rem", color: "var(--color-primario)" };
+
+ return (
+
+
+
+
+
+
+
+
+ MI FAMILIA
+ (Ruta para crear rutina)
+
+
+
+
+
+
+ CUIDADOR/A
+ (Aqui se gestiona Rutina)
+
+
+
+
+
+ ZZZYNC
+ (Visualizacion de progreso)
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/front/pages/Layout.jsx b/src/front/pages/Layout.jsx
index 9bfa31325c..a53d7ec034 100644
--- a/src/front/pages/Layout.jsx
+++ b/src/front/pages/Layout.jsx
@@ -3,13 +3,22 @@ import ScrollToTop from "../components/ScrollToTop"
import { Navbar } from "../components/Navbar"
import { Footer } from "../components/Footer"
-// Base component that maintains the navbar and footer throughout the page and the scroll to top functionality.
export const Layout = () => {
return (
-
-
-
+
+
)
}
\ 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..05c83349a1
--- /dev/null
+++ b/src/front/pages/Login.jsx
@@ -0,0 +1,88 @@
+import React, { useState } from "react";
+import { useNavigate, Link } from "react-router-dom";
+import useGlobalReducer from "../hooks/useGlobalReducer";
+import logoApp from "../assets/Logo Baby Zzync 1 - vers blanca.png";
+
+export const Login = () => {
+ const { store, actions, dispatch } = useGlobalReducer();
+ const [email, setEmail] = useState("");
+ const [password, setPassword] = useState("");
+ const navigate = useNavigate();
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+ try {
+ const response = await fetch(`${import.meta.env.VITE_BACKEND_URL}/api/login`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ email, password })
+ });
+
+ if (response.ok) {
+ const data = await response.json();
+
+ localStorage.setItem("token", data.token);
+ localStorage.setItem("user", JSON.stringify(data.user));
+
+ dispatch({ type: 'set_user', payload: data.user });
+
+ await actions.loadParentData();
+
+ navigate("/home");
+ } else {
+ const errorData = await response.json();
+ alert(errorData.msg || "Correo o contraseña incorrectos");
+ }
+ } catch (error) {
+ console.error("Error en login:", error);
+ alert("No se pudo conectar con el servidor.");
+ }
+ };
+
+ return (
+
+
+
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/front/pages/Menupadre.jsx b/src/front/pages/Menupadre.jsx
new file mode 100644
index 0000000000..19a304276f
--- /dev/null
+++ b/src/front/pages/Menupadre.jsx
@@ -0,0 +1,104 @@
+import React, { useState, useEffect } from "react";
+import { Link } from "react-router-dom";
+import useGlobalReducer from "../hooks/useGlobalReducer";
+import { Cardhijo } from "../components/Cardhijo.jsx";
+import { Carduser } from "../components/Carduser.jsx";
+import { Cardautorizado } from "../components/Cardautorizado.jsx";
+import { HeaderApp2 } from "../components/HeaderApp2.jsx";
+
+export const Menupadre = () => {
+ const { store, actions } = useGlobalReducer();
+ const [activeTab, setActiveTab] = useState("hijos");
+
+ useEffect(() => {
+ actions.loadParentData();
+ }, []);
+
+ return (
+
+
+
+
+
+
+
+
+ setActiveTab("hijos")}
+ style={{
+ fontSize: "0.85rem",
+ backgroundColor: activeTab === "hijos" ? "var(--color-primario)" : "transparent",
+ transition: "0.3s"
+ }}
+ >
+ Mis Hijos
+
+
+
+ setActiveTab("autorizados")}
+ style={{
+ fontSize: "0.85rem",
+ backgroundColor: activeTab === "autorizados" ? "var(--color-primario)" : "transparent",
+ transition: "0.3s"
+ }}
+ >
+ Autorizados
+
+
+
+
+
+
+ {activeTab === "hijos" && (
+
+ {store.hijos && store.hijos.length > 0 ? (
+ store.hijos.map(hijo => (
+
+ ))
+ ) : (
+
+
+
No hay hijos registrados.
+
+ )}
+
+ )}
+
+ {activeTab === "autorizados" && (
+
+ {store.autorizados && store.autorizados.length > 0 ? (
+ store.autorizados.map(auth => (
+
+ ))
+ ) : (
+
+
+
No hay personas autorizadas.
+
+ )}
+
+ )}
+
+
+
+ {activeTab === "hijos" ? (
+
+
+ Añadir Hijo
+
+
+ ) : (
+
+
+ Añadir Autorizado
+
+
+ )}
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/front/pages/Registro.jsx b/src/front/pages/Registro.jsx
new file mode 100644
index 0000000000..b2f03f89a7
--- /dev/null
+++ b/src/front/pages/Registro.jsx
@@ -0,0 +1,190 @@
+import React, { useState } from "react";
+import logoApp from "../assets/Logo Baby Zzync 1.png";
+import { Link, useNavigate } from "react-router-dom";
+import Cloudinary from "../components/Cloudinary";
+import GoogleInput from "../components/GoogleInput";
+
+export const Registro = () => {
+ const navigate = useNavigate();
+ const [showPassword, setShowPassword] = useState(false);
+ const [showConfirmPassword, setShowConfirmPassword] = useState(false);
+ const [validated, setValidated] = useState(false);
+
+ const [formData, setFormData] = useState({
+ email: "", password: "", confirmPassword: "", nombre: "", apellidos: "",
+ edad: "", direccionHogar: "", direccionTrabajo: "", prefijo: "+34",
+ telefono: "", fotoPerfil: ""
+ });
+
+ const handleChange = (e) => {
+ const { name, value } = e.target;
+ if (name === "telefono") {
+ const onlyNums = value.replace(/[^0-9]/g, "");
+ setFormData({ ...formData, [name]: onlyNums });
+ } else {
+ setFormData({ ...formData, [name]: value });
+ }
+ };
+
+ const handleImageUploaded = (url) => setFormData({ ...formData, fotoPerfil: url });
+
+ const handleHogarSelect = (place) => {
+ setFormData({ ...formData, direccionHogar: place.formatted_address });
+ };
+
+ const handleTrabajoSelect = (place) => {
+ setFormData({ ...formData, direccionTrabajo: place.formatted_address });
+ };
+
+ const passwordsMatch = formData.password === formData.confirmPassword;
+ const showMatchError = formData.confirmPassword.length > 0 && !passwordsMatch;
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+ setValidated(true);
+
+ if (!formData.fotoPerfil) return;
+
+ if (!passwordsMatch) {
+ alert("Las contraseñas no coinciden");
+ return;
+ }
+
+ const dataToSend = {
+ ...formData,
+ telefonoCompleto: `${formData.prefijo} ${formData.telefono}`
+ };
+
+ try {
+ const response = await fetch(`${import.meta.env.VITE_BACKEND_URL}/api/registro`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(dataToSend),
+ });
+
+ if (response.ok) {
+ navigate("/");
+ } else {
+ const data = await response.json();
+ alert(data.msg || "Error al registrar la cuenta");
+ }
+ } catch (error) {
+ console.error("Error:", error);
+ alert("Error de conexión con el servidor");
+ }
+ };
+
+ return (
+
+
+
+
+
+
+
+ {validated && !formData.fotoPerfil && (
+ Campo Obligatorio
+ )}
+
+
+
+
+
+
+
+
+
+ {validated && !formData.email &&
Campo Obligatorio }
+
+
+
+
+
+
+ setShowPassword(!showPassword)} style={{cursor:"pointer", color:"var(--color-descanso)"}}>
+
+ {validated && !formData.password &&
Campo Obligatorio }
+
+
+
+
+
+
+ setShowConfirmPassword(!showConfirmPassword)} style={{cursor:"pointer", color:"var(--color-descanso)"}}>
+
+ {showMatchError &&
Las contraseñas no coinciden }
+
+
+
+
+
+
+
+ {validated && !formData.nombre &&
Campo Obligatorio }
+
+
+
+
+
+
+
+ {validated && !formData.apellidos &&
Campo Obligatorio }
+
+
+
+
+
+
+
+ {validated && !formData.edad &&
Campo Obligatorio }
+
+
+ setFormData({ ...formData, direccionHogar: e.target.value })}
+ required={true}
+ />
+
+ setFormData({ ...formData, direccionTrabajo: e.target.value })}
+ required={false}
+ />
+
+
+
+
+
+ 🇪🇸 +34
+ 🇲🇽 +52
+ 🇺🇸 +1
+
+
+
+ {validated && !formData.telefono &&
Campo Obligatorio }
+
+
+
+ ¿Ya tienes una cuenta?
+
+
+ Crear Cuenta
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/front/pages/Rutinas.jsx b/src/front/pages/Rutinas.jsx
new file mode 100644
index 0000000000..7cb0c386b7
--- /dev/null
+++ b/src/front/pages/Rutinas.jsx
@@ -0,0 +1,235 @@
+import React, { useState, useEffect } from "react";
+import { useNavigate, useLocation } from "react-router-dom";
+import Swal from "sweetalert2";
+import { HeaderApp4 } from "../components/HeaderApp4";
+import useGlobalReducer from "../hooks/useGlobalReducer";
+
+export const Rutinas = () => {
+ const { store } = useGlobalReducer();
+ const navigate = useNavigate();
+ const location = useLocation();
+ const [rutinas, setRutinas] = useState([]);
+ const [activeRutinaId, setActiveRutinaId] = useState(null);
+
+ const [showModal, setShowModal] = useState(false);
+ const [selectedRutina, setSelectedRutina] = useState(null);
+ const [selectedHijos, setSelectedHijos] = useState([]);
+
+ const apiUrl = import.meta.env.VITE_BACKEND_URL || "http://localhost:3001";
+ const hijoSeleccionado = location.state?.hijo;
+
+ const handleBack = () => {
+ navigate(-1);
+ };
+
+ useEffect(() => {
+ const token = localStorage.getItem("token");
+ if (!token) {
+ navigate("/");
+ return;
+ }
+
+ fetch(`${apiUrl}/api/rutinas`, {
+ method: "GET",
+ headers: { "Authorization": `Bearer ${token}` }
+ })
+ .then(res => {
+ if (res.status === 401) {
+ localStorage.removeItem("token");
+ navigate("/");
+ return;
+ }
+ return res.json();
+ })
+ .then(data => { if (data) setRutinas(data); })
+ .catch(err => console.error(err));
+ }, [navigate, apiUrl]);
+
+ const toggleMenu = (id) => {
+ setActiveRutinaId(activeRutinaId === id ? null : id);
+ };
+
+ const handleDelete = (id) => {
+ Swal.fire({
+ title: "¿DESEAS ELIMINAR ESTA CARD?",
+ icon: "warning",
+ showCancelButton: true,
+ confirmButtonColor: "#d33",
+ cancelButtonColor: "#3085d6",
+ confirmButtonText: "SÍ",
+ cancelButtonText: "NO",
+ customClass: { popup: 'my-custom-popup' }
+ }).then((result) => {
+ if (result.isConfirmed) {
+ const token = localStorage.getItem("token");
+ fetch(`${apiUrl}/api/rutinas/${id}`, {
+ method: "DELETE",
+ headers: { "Authorization": `Bearer ${token}` }
+ })
+ .then(res => {
+ if(res.ok) {
+ setRutinas(rutinas.filter(rutina => rutina.id !== id));
+ Swal.fire({ title: "¡Eliminado!", icon: "success", timer: 1500, showConfirmButton: false });
+ }
+ })
+ .catch(err => console.error("Error borrando:", err));
+ }
+ });
+ };
+
+ const openAsignarModal = (rutina) => {
+ setSelectedRutina(rutina);
+ setSelectedHijos([]);
+ setShowModal(true);
+ };
+
+ const handleCheckboxChange = (hijoId) => {
+ setSelectedHijos(prev =>
+ prev.includes(hijoId)
+ ? prev.filter(id => id !== hijoId)
+ : [...prev, hijoId]
+ );
+ };
+
+ const handleGuardarAsignacion = () => {
+ const token = localStorage.getItem("token");
+ if (selectedHijos.length === 0) {
+ Swal.fire("Atención", "Selecciona al menos un hijo", "info");
+ return;
+ }
+
+ fetch(`${apiUrl}/api/asignar-rutina`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "Authorization": `Bearer ${token}`
+ },
+ body: JSON.stringify({
+ rutina_id: selectedRutina.id,
+ hijo_ids: selectedHijos
+ })
+ })
+ .then(res => {
+ if (res.ok) {
+ setShowModal(false);
+ Swal.fire({ title: "¡Listo!", text: "Asignación guardada correctamente", icon: "success", timer: 1500, showConfirmButton: false });
+ } else {
+ Swal.fire("Error", "No se pudo realizar la asignación", "error");
+ }
+ })
+ .catch(err => console.error(err));
+ };
+
+ return (
+
+
+
+
+
+
RUTINAS
+
+
+
+ {rutinas.length === 0 ? (
+
No tienes rutinas creadas.
+ ) : (
+ rutinas.map((rutina) => (
+
+
toggleMenu(rutina.id)}
+ >
+
+
{rutina.nombre}
+
{rutina.detalles}
+
+
+
+ {activeRutinaId === rutina.id && (
+
+ openAsignarModal(rutina)}
+ >
+ ASIGNAR
+
+ navigate(`/familia-rutina/${rutina.id}`)}>EDITAR
+ handleDelete(rutina.id)}>BORRAR
+
+ )}
+
+ ))
+ )}
+
+
+
+ navigate("/Crear-rutina", { state: { hijo: hijoSeleccionado } })}
+ className="btn w-100 py-3"
+ style={{
+ backgroundColor: "var(--color-primario)",
+ color: "white",
+ borderRadius: "50px",
+ fontWeight: "bold",
+ fontSize: "1rem",
+ boxShadow: "0 4px 12px rgba(72, 12, 168, 0.3)"
+ }}
+ >
+ CREAR NUEVA RUTINA
+
+
+
+
+ {showModal && (
+
+
+
+
+
+ Asignar: {selectedRutina?.nombre}
+
+ setShowModal(false)}>
+
+
+
Selecciona a los hijos para esta rutina:
+
+ {store.hijos && store.hijos.length > 0 ? (
+ store.hijos.map(hijo => (
+
+ handleCheckboxChange(hijo.id)}
+ />
+ {hijo.nombre} {hijo.apellido}
+
+ ))
+ ) : (
+
No hay hijos registrados.
+ )}
+
+
+
+
+ GUARDAR ASIGNACIÓN
+
+
+
+
+
+ )}
+
+ );
+};
\ No newline at end of file
diff --git a/src/front/pages/VistaCuidador.jsx b/src/front/pages/VistaCuidador.jsx
new file mode 100644
index 0000000000..aa3c158b78
--- /dev/null
+++ b/src/front/pages/VistaCuidador.jsx
@@ -0,0 +1,151 @@
+import React, { useState, useEffect } from "react";
+import { useNavigate } from "react-router-dom";
+import useGlobalReducer from "../hooks/useGlobalReducer";
+import { HeaderApp } from "../components/HeaderApp";
+import Swal from "sweetalert2";
+
+export const VistaCuidador = () => {
+ const { actions } = useGlobalReducer();
+ const navigate = useNavigate();
+ const [rutinasAsignadas, setRutinasAsignadas] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const apiUrl = import.meta.env.VITE_BACKEND_URL || "http://localhost:3001";
+
+ const fetchRutinasCompartidas = async () => {
+ try {
+ const token = localStorage.getItem("token");
+ const response = await fetch(`${apiUrl}/api/cuidador/rutinas`, {
+ headers: {
+ "Authorization": `Bearer ${token}`,
+ "Content-Type": "application/json"
+ }
+ });
+
+ if (response.ok) {
+ const data = await response.json();
+ setRutinasAsignadas(data);
+ }
+ } catch (error) {
+ console.error("Error al obtener rutinas:", error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ useEffect(() => {
+ fetchRutinasCompartidas();
+ }, [apiUrl]);
+
+ const handleEliminarRutina = async (idAsignacion) => {
+ const result = await Swal.fire({
+ title: "¿Estás seguro?",
+ text: "Esta rutina dejará de aparecer en tu lista de cuidador.",
+ icon: "warning",
+ showCancelButton: true,
+ confirmButtonColor: "var(--color-primario)",
+ cancelButtonColor: "#d33",
+ confirmButtonText: "Sí, eliminar",
+ cancelButtonText: "Cancelar",
+ borderRadius: "15px",
+ });
+
+ if (result.isConfirmed) {
+ const success = await actions.deleteRutinaCompartida(idAsignacion);
+
+ if (success) {
+ setRutinasAsignadas(prev => prev.filter(item => item.id !== idAsignacion));
+ Swal.fire({
+ title: "¡Eliminado!",
+ text: "La rutina ha sido quitada de tu vista.",
+ icon: "success",
+ confirmButtonColor: "var(--color-primario)",
+ timer: 2000
+ });
+ } else {
+ Swal.fire({
+ title: "Error",
+ text: "No se pudo eliminar la rutina. Inténtalo de nuevo.",
+ icon: "error",
+ confirmButtonColor: "var(--color-primario)"
+ });
+ }
+ }
+ };
+
+ return (
+
+
navigate("/home")}
+ />
+
+
+
+
+
Área del Cuidador
+
Rutinas compartidas contigo
+
+
+
+ {loading ? (
+
+
+
Cargando rutinas...
+
+ ) : rutinasAsignadas.length === 0 ? (
+
+
+
No tienes rutinas asignadas todavía.
+
+ ) : (
+ rutinasAsignadas.map((item) => (
+
+
+
+ {item.hijo.fotoUrl ? (
+
+ ) : (
+
+ )}
+
+
+
{item.rutina.nombre}
+
+ {item.hijo.nombre} {item.hijo.apellido}
+
+
+
+
+ navigate(`/detalle-rutina-hijo/${item.rutina.id}`, {
+ state: {
+ rutina: item.rutina,
+ hijo: item.hijo,
+ esCuidador: true
+ }
+ })}
+ >
+ Ver
+
+ handleEliminarRutina(item.id)}
+ >
+
+
+
+
+
+ ))
+ )}
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/front/routes.jsx b/src/front/routes.jsx
index 0557df6141..2ffbc65035 100644
--- a/src/front/routes.jsx
+++ b/src/front/routes.jsx
@@ -1,5 +1,3 @@
-// Import necessary components and functions from react-router-dom.
-
import {
createBrowserRouter,
createRoutesFromElements,
@@ -9,22 +7,44 @@ import { Layout } from "./pages/Layout";
import { Home } from "./pages/Home";
import { Single } from "./pages/Single";
import { Demo } from "./pages/Demo";
+import { Registro } from "./pages/Registro";
+import { Login } from "./pages/Login";
+import { FamiliaRutina } from "./pages/FamiliaRutina";
+import { Menupadre } from "./pages/Menupadre";
+import { Addhijo } from "./pages/Addhijo.jsx";
+import { AddAutorizado } from "./pages/AddAutorizado.jsx";
+import ProtectedRoute from "./components/ProtectedRoute";
+import { AsignarRutina } from "./pages/AsignarRutina.jsx";
+import { Rutinas } from "./pages/Rutinas.jsx";
+import { CrearRutina } from "./pages/CrearRutina.jsx";
+import { DetalleRutinaHijo } from "./pages/DetalleRutinaHijo.jsx";
+import { VistaCuidador } from "./pages/VistaCuidador.jsx";
export const router = createBrowserRouter(
createRoutesFromElements(
- // CreateRoutesFromElements function allows you to build route elements declaratively.
- // Create your routes here, if you want to keep the Navbar and Footer in all views, add your new routes inside the containing Route.
- // Root, on the contrary, create a sister Route, if you have doubts, try it!
- // Note: keep in mind that errorElement will be the default page when you don't get a route, customize that page to make your project more attractive.
- // Note: The child paths of the Layout element replace the Outlet component with the elements contained in the "element" attribute of these child paths.
-
- // Root Route: All navigation will start from here.
} errorElement={Not found! } >
+
+ } />
+ } />
+
+ } />
+ } />
+ } />
+ } />
+
+ } />
+
+ } />
+ } />
+ } />
+ } />
+
+ } />
+ } />
+
+
+ } />
- {/* Nested Routes: Defines sub-routes within the BaseHome component. */}
- } />
- } /> {/* Dynamic route for single items */}
- } />
)
);
\ No newline at end of file
diff --git a/src/front/store.js b/src/front/store.js
index 3062cd222d..1d06cfff2a 100644
--- a/src/front/store.js
+++ b/src/front/store.js
@@ -1,38 +1,46 @@
-export const initialStore=()=>{
- return{
- message: null,
- todos: [
- {
- id: 1,
- title: "Make the bed",
- background: null,
- },
- {
- id: 2,
- title: "Do my homework",
- background: null,
- }
- ]
- }
-}
+export const initialStore = () => {
+ return {
+ hijos: [],
+ autorizados: [],
+ user: JSON.parse(localStorage.getItem("user")) || null
+ };
+};
-export default function storeReducer(store, action = {}) {
- switch(action.type){
- case 'set_hello':
- return {
- ...store,
- message: action.payload
- };
-
- case 'add_task':
+export default function storeReducer(store, action) {
+ switch (action.type) {
+ case 'set_hijos':
+ return { ...store, hijos: action.payload };
+
+ case 'add_hijo':
+ return { ...store, hijos: [...store.hijos, action.payload] };
+
+ case 'delete_hijo':
+ return { ...store, hijos: store.hijos.filter(hijo => hijo.id !== action.payload) };
- const { id, color } = action.payload
+ case 'set_autorizados':
+ return { ...store, autorizados: action.payload };
+ case 'add_autorizado':
+ return { ...store, autorizados: [...store.autorizados, action.payload] };
+
+ case 'delete_autorizado':
+ return { ...store, autorizados: store.autorizados.filter(auth => auth.id !== action.payload) };
+
+ case 'set_user':
+ return { ...store, user: action.payload };
+
+ case 'logout':
+ localStorage.removeItem("user");
+ localStorage.removeItem("token");
+ localStorage.clear();
return {
...store,
- todos: store.todos.map((todo) => (todo.id === id ? { ...todo, background: color } : todo))
+ user: null,
+ hijos: [],
+ autorizados: []
};
+
default:
- throw Error('Unknown action.');
- }
-}
+ return store;
+ }
+}
\ No newline at end of file