preliminar beta
BIN
Documents_ref/Formha_logos/archivos logo.zip
Normal file
8130
Documents_ref/Formha_logos/logo_final.ai
Normal file
123
Documents_ref/Formha_logos/logo_final.svg
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
version="1.1"
|
||||||
|
id="svg2"
|
||||||
|
width="741.34265"
|
||||||
|
height="687.81335"
|
||||||
|
viewBox="0 0 741.34265 687.81335"
|
||||||
|
sodipodi:docname="logo_final.ai"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs6">
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath16">
|
||||||
|
<path
|
||||||
|
d="M 0,515.86 H 556.007 V 0 H 0 Z"
|
||||||
|
id="path14" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview4"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#000000"
|
||||||
|
borderopacity="0.25"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1" />
|
||||||
|
<g
|
||||||
|
id="g8"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
inkscape:label="logo_final"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,0,687.81333)">
|
||||||
|
<g
|
||||||
|
id="g10">
|
||||||
|
<g
|
||||||
|
id="g12"
|
||||||
|
clip-path="url(#clipPath16)">
|
||||||
|
<g
|
||||||
|
id="g18"
|
||||||
|
transform="translate(278.0034,257.5454)">
|
||||||
|
<path
|
||||||
|
d="m 0,0 c -27.777,0 -50.295,22.518 -50.295,50.295 0,27.777 22.518,50.295 50.295,50.295 27.777,0 50.295,-22.518 50.295,-50.295 C 50.295,22.518 27.777,0 0,0 m 0,161.68 c -61.516,0 -111.385,-49.869 -111.385,-111.385 0,-61.516 49.869,-111.385 111.385,-111.385 61.517,0 111.385,49.869 111.385,111.385 C 111.385,111.811 61.517,161.68 0,161.68"
|
||||||
|
style="fill:#293172;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
id="path20" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g22"
|
||||||
|
transform="translate(278.0034,257.5454)">
|
||||||
|
<path
|
||||||
|
d="m 0,0 c -27.777,0 -50.295,22.518 -50.295,50.295 0,27.777 22.518,50.295 50.295,50.295 27.777,0 50.295,-22.518 50.295,-50.295 C 50.295,22.518 27.777,0 0,0 m 0,153.912 c -57.226,0 -103.617,-46.391 -103.617,-103.617 0,-57.226 46.391,-103.617 103.617,-103.617 57.226,0 103.617,46.391 103.617,103.617 0,57.226 -46.391,103.617 -103.617,103.617"
|
||||||
|
style="fill:#189dd9;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
id="path24" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g26"
|
||||||
|
transform="translate(232.5616,286.2715)">
|
||||||
|
<path
|
||||||
|
d="m 0,0 c -3.109,6.537 -4.853,13.849 -4.853,21.569 0,7.729 1.747,15.047 4.862,21.59 7.326,24.212 29.478,42.277 74.197,42.277 8.439,0 16.581,-1.262 24.252,-3.605 2.724,-0.831 4.536,2.798 2.221,4.456 -17.261,12.362 -38.405,19.644 -61.255,19.644 -54.683,0 -99.628,-41.658 -104.852,-94.968 5.343,-56.539 52.932,-100.779 110.87,-100.779 18.079,0 35.136,4.335 50.234,11.979 -6.849,-1.829 -14.044,-2.81 -21.47,-2.81 C 42.946,-80.647 9.739,-40.708 0,0"
|
||||||
|
style="fill:#a4195b;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
id="path28" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g30"
|
||||||
|
transform="translate(381.6201,289.5588)">
|
||||||
|
<path
|
||||||
|
d="m 0,0 c 0,8.188 -1.537,16.013 -4.312,23.224 -0.146,0.381 -0.295,0.76 -0.448,1.137 -0.218,0.544 -0.452,1.079 -0.683,1.616 -0.724,1.677 -1.49,3.333 -2.344,4.936 -0.005,-0.029 -0.011,-0.058 -0.015,-0.087 -10.954,20.173 -32.324,33.869 -56.893,33.869 -5.355,0 -10.554,-0.663 -15.531,-1.89 16.618,-8.749 27.75,-26.517 26.854,-46.801 -1.143,-25.894 -22.283,-46.94 -48.182,-47.976 -20.408,-0.816 -38.239,10.541 -46.857,27.392 -9.913,13.159 -15.795,29.525 -15.795,47.27 0,5.351 0.54,10.575 1.562,15.624 0.532,2.628 -2.878,4.12 -4.503,1.988 -12.623,-16.564 -19.832,-37.474 -18.957,-60.104 1.889,-48.834 41.94,-88.287 90.797,-89.481 45.027,-1.102 83.018,29.749 92.971,71.479 0.327,1.369 0.614,2.754 0.878,4.146 0.693,3.22 1.148,6.527 1.342,9.905 0.011,0.141 0.029,0.28 0.04,0.421 C -0.032,-2.335 0,-1.335 0,-0.327 0,-0.278 -0.005,-0.23 -0.005,-0.181 -0.005,-0.12 0,-0.061 0,0"
|
||||||
|
style="fill:#7db928;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
id="path32" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g34"
|
||||||
|
transform="translate(116.363,97.5947)">
|
||||||
|
<path
|
||||||
|
d="M 0,0 V 56.239 H 38.554 V 46.726 H 11.355 V 33.413 H 34.833 V 23.899 H 11.355 L 11.355,0 Z"
|
||||||
|
style="fill:#747474;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
id="path36" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g38"
|
||||||
|
transform="translate(173.6766,125.7527)">
|
||||||
|
<path
|
||||||
|
d="m 0,0 c 0,-6.368 1.471,-11.196 4.412,-14.481 2.94,-3.288 6.675,-4.93 11.202,-4.93 4.526,0 8.241,1.63 11.143,4.89 2.903,3.262 4.355,8.153 4.355,14.674 0,6.445 -1.413,11.253 -4.239,14.425 -2.826,3.171 -6.579,4.757 -11.259,4.757 -4.681,0 -8.454,-1.606 -11.317,-4.814 C 1.432,11.31 0,6.47 0,0 m -11.701,-0.384 c 0,5.729 0.857,10.537 2.571,14.425 1.278,2.864 3.024,5.434 5.237,7.711 2.211,2.275 4.635,3.963 7.269,5.064 3.504,1.482 7.544,2.224 12.122,2.224 8.287,0 14.917,-2.57 19.892,-7.71 4.973,-5.141 7.461,-12.29 7.461,-21.446 0,-9.079 -2.469,-16.183 -7.404,-21.31 -4.936,-5.127 -11.534,-7.692 -19.795,-7.692 -8.363,0 -15.013,2.552 -19.949,7.655 -4.936,5.101 -7.404,12.128 -7.404,21.079"
|
||||||
|
style="fill:#747474;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
id="path40" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g42"
|
||||||
|
transform="translate(236.7829,130.0493)">
|
||||||
|
<path
|
||||||
|
d="m 0,0 h 8.401 c 5.448,0 8.849,0.23 10.205,0.691 1.355,0.46 2.417,1.253 3.184,2.378 0.767,1.125 1.151,2.532 1.151,4.22 0,1.892 -0.506,3.419 -1.515,4.584 -1.011,1.164 -2.437,1.9 -4.278,2.207 -0.921,0.126 -3.683,0.191 -8.286,0.191 l -8.862,0 z m -11.355,-32.455 v 56.24 h 23.9 c 6.009,0 10.376,-0.506 13.101,-1.515 2.723,-1.011 4.903,-2.807 6.54,-5.39 1.636,-2.584 2.455,-5.538 2.455,-8.863 0,-4.218 -1.241,-7.704 -3.721,-10.453 -2.481,-2.75 -6.19,-4.483 -11.125,-5.199 2.455,-1.432 4.482,-3.004 6.081,-4.718 1.598,-1.713 3.753,-4.757 6.464,-9.13 l 6.866,-10.972 h -13.58 l -8.209,12.237 c -2.916,4.375 -4.911,7.13 -5.985,8.268 -1.074,1.138 -2.212,1.919 -3.414,2.34 -1.203,0.423 -3.108,0.633 -5.716,0.633 H 0 v -23.478 z"
|
||||||
|
style="fill:#747474;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
id="path44" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g46"
|
||||||
|
transform="translate(281.9739,97.5947)">
|
||||||
|
<path
|
||||||
|
d="M 0,0 V 56.239 H 16.995 L 27.199,17.877 37.288,56.239 H 54.321 V 0 H 43.772 V 44.27 L 32.608,0 H 21.675 L 10.55,44.27 V 0 Z"
|
||||||
|
style="fill:#747474;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
id="path48" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g50"
|
||||||
|
transform="translate(347.6121,97.5947)">
|
||||||
|
<path
|
||||||
|
d="M 0,0 V 56.239 H 11.355 V 34.105 h 22.25 V 56.239 H 44.96 V 0 H 33.605 V 24.591 H 11.355 L 11.355,0 Z"
|
||||||
|
style="fill:#7db928;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
id="path52" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
d="m 424.145,154.792 h 9.284 v -9.245 h -9.284 z m 2.571,-37.632 c -1.381,-0.461 -3.568,-1.011 -6.56,-1.651 -2.993,-0.639 -4.948,-1.265 -5.869,-1.879 -1.408,-0.997 -2.111,-2.264 -2.111,-3.798 0,-1.509 0.563,-2.814 1.688,-3.912 1.125,-1.101 2.558,-1.65 4.297,-1.65 1.943,0 3.798,0.639 5.562,1.918 1.305,0.972 2.161,2.16 2.571,3.568 0.281,0.921 0.422,2.672 0.422,5.255 z m -19.067,37.632 h 9.246 v -9.245 h -9.246 z m 4.643,-28.886 -9.783,1.765 c 1.1,3.938 2.992,6.854 5.677,8.747 2.686,1.892 6.676,2.838 11.97,2.838 4.808,0 8.388,-0.57 10.741,-1.707 2.353,-1.138 4.009,-2.583 4.968,-4.334 0.959,-1.753 1.439,-4.969 1.439,-9.648 l -0.115,-12.583 c 0,-3.582 0.173,-6.222 0.517,-7.922 0.346,-1.702 0.991,-3.524 1.938,-5.467 h -10.665 c -0.281,0.716 -0.627,1.776 -1.035,3.183 -0.181,0.639 -0.307,1.062 -0.385,1.267 -1.842,-1.791 -3.811,-3.134 -5.907,-4.029 -2.098,-0.894 -4.335,-1.342 -6.714,-1.342 -4.194,0 -7.499,1.137 -9.917,3.415 -2.416,2.275 -3.624,5.153 -3.624,8.631 0,2.302 0.549,4.354 1.649,6.157 1.1,1.803 2.64,3.184 4.623,4.144 1.981,0.958 4.84,1.796 8.574,2.512 5.038,0.946 8.528,1.828 10.473,2.648 v 1.074 c 0,2.07 -0.513,3.547 -1.534,4.43 -1.025,0.882 -2.955,1.323 -5.794,1.323 -1.918,0 -3.413,-0.378 -4.487,-1.132 -1.074,-0.753 -1.946,-2.077 -2.609,-3.97"
|
||||||
|
style="fill:#189dd9;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
id="path54" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 8.2 KiB |
10
Documents_ref/ayuda.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
https://www.metricser.com/segmentacion-del-electorado/
|
||||||
|
https://htmlstream.com/preview/front-v3.2/documentation/aos.html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pg_dump -U postgres -d forma -F c -f ./forma.backup
|
||||||
|
|
||||||
|
pg_dump -U postgres -d forma -F p -f .forma.sql
|
||||||
|
|
||||||
|
|
99
Documents_ref/backup_tmp_user.html
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
|
<title>
|
||||||
|
{% block title %}{{ title | default('FORMHä') }}{% endblock %}
|
||||||
|
</title>
|
||||||
|
|
||||||
|
<!-- {# i bootstrap #} -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||||
|
<!-- {# icons #}} -->
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||||
|
<!-- {# f bootstrap #} -->
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
<!-- {# i navbar css #} -->
|
||||||
|
<link rel="stylesheet" href="{{url_for('static', filename='template/tmp.css')}}">
|
||||||
|
<!-- {# navbar #} -->
|
||||||
|
<link rel="stylesheet" href="{{url_for('static', filename='template/navbar.css')}}">
|
||||||
|
<!-- {# f navbar css #} -->
|
||||||
|
|
||||||
|
<!-- {# i notify css #} -->
|
||||||
|
<!-- {# doc: https://github.com/simple-notify/simple-notify?tab=readme-ov-file #} -->
|
||||||
|
<!-- CSS -->
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/simple-notify/dist/simple-notify.css" />
|
||||||
|
<!-- JS -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/simple-notify/dist/simple-notify.min.js"></script>
|
||||||
|
<!-- {# f notify js #} -->
|
||||||
|
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<!-- <body> -->
|
||||||
|
<body>
|
||||||
|
|
||||||
|
{% block navbar %}
|
||||||
|
<!-- {# {% include 'comps/navbar_usr.html' %} #} -->
|
||||||
|
<nav class="navbar navbar-expand-lg bg-body-tertiary navbar-custom">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<a class="navbar-brand" href="{{ url_for('home') }}">
|
||||||
|
{% include 'comps/formha.html' %}
|
||||||
|
</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||||
|
<ul class="navbar-nav me-auto mb-2 mb-lg-0 effect-3">
|
||||||
|
|
||||||
|
<li class="nav-item dropdown">
|
||||||
|
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
<i class="bi bi-person-circle"></i> {{nombre}}
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
|
||||||
|
<li><a class="dropdown-item" href="#"><i class="bi bi-file-earmark-person-fill"></i> Usuarios</a></li>
|
||||||
|
<li><hr class="dropdown-divider"></li>
|
||||||
|
<!-- <li><a class="dropdown-item" href="#">Something else here</a></li> -->
|
||||||
|
<li>
|
||||||
|
<a class="dropdown-item" href="{{ url_for('logout') }}">
|
||||||
|
<i class="bi bi-door-open-fill"></i> Logout
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
{% endblock navbar %}
|
||||||
|
<!-- <main> -->
|
||||||
|
<main>
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock body %}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
{% include 'comps/footer.html' %}
|
||||||
|
<!-- {# i bootstrap js #} -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
|
||||||
|
<!-- {# f bootstrap js #} -->
|
||||||
|
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
{% include 'comps/notification.html' %}
|
||||||
|
|
||||||
|
{% if f_mnsj %}
|
||||||
|
<script>
|
||||||
|
simpleNotification("{{ f_mnsj.title }}", "{{ f_mnsj.body }}", "{{ f_mnsj.typeAlert }}");
|
||||||
|
</script>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock js %}
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -14,29 +14,50 @@ CREATE DATABASE forma;
|
|||||||
|
|
||||||
\c forma;
|
\c forma;
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE contact (
|
CREATE TABLE contact (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
fecha VARCHAR(10),
|
full_date_time TIMESTAMP WITH TIME ZONE,
|
||||||
hora VARCHAR(10),
|
|
||||||
nombre VARCHAR(50),
|
nombre VARCHAR(50),
|
||||||
apellido VARCHAR(100),
|
apellido VARCHAR(100),
|
||||||
email VARCHAR(150),
|
email VARCHAR(150),
|
||||||
estado VARCHAR(30),
|
estado VARCHAR(50),
|
||||||
num_tel VARCHAR(20),
|
num_tel VARCHAR(20),
|
||||||
size_co VARCHAR(40),
|
size_co VARCHAR(40),
|
||||||
rol_contacto VARCHAR(50),
|
rol_contacto VARCHAR(50),
|
||||||
industry_type VARCHAR(40),
|
industry_type VARCHAR(40),
|
||||||
tipo_req VARCHAR(255)
|
tipo_req VARCHAR(255),
|
||||||
|
status VARCHAR(50)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE users(
|
CREATE TABLE users(
|
||||||
id VARCHAR(25),
|
id VARCHAR(25),
|
||||||
nombre VARCHAR(50),
|
nombre VARCHAR(50),
|
||||||
apellido VARCHAR(100),
|
apellido VARCHAR(100),
|
||||||
|
genero VARCHAR(2),
|
||||||
email VARCHAR(150),
|
email VARCHAR(150),
|
||||||
pswd VARCHAR(100),
|
pswd VARCHAR(100),
|
||||||
isAdmin boolean
|
lst_conn TIMESTAMP WITH TIME ZONE,
|
||||||
|
is_admin boolean
|
||||||
);
|
);
|
||||||
|
|
||||||
INSERT INTO users (id, nombre, apellido, email, pswd, isAdmin) VALUES
|
INSERT INTO users (id, nombre, apellido, genero, email, pswd, is_admin) VALUES
|
||||||
('4HlOjqJ6jLISxNQIbs2Hzz', 'David', 'Itehua Xalamihua', 'davidix1991@gmail.com', '$2b$12$dbJWK5mv89PszxPeXlql5Otd8vv7kz6M44JnKZcrwJdKoovayiqEm', true);
|
('4HlOjqJ6jLISxNQIbs2Hzz', 'David', 'Itehua Xalamihua', 'M', 'davidix1991@gmail.com', '$2b$12$dbJWK5mv89PszxPeXlql5Otd8vv7kz6M44JnKZcrwJdKoovayiqEm', false),
|
||||||
|
('4FyJhu54R6ARH2FlmroTxl', 'David', 'Itehua Xalamihua', 'M', 'davicho1991@live.com', '$2b$12$dbJWK5mv89PszxPeXlql5Otd8vv7kz6M44JnKZcrwJdKoovayiqEm', true );
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE posts (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
id_usr VARCHAR(25),
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE,
|
||||||
|
updated_at TIMESTAMP WITH TIME ZONE,
|
||||||
|
title VARCHAR(150) NOT NULL,
|
||||||
|
body TEXT NOT NULL,
|
||||||
|
body_no_img TEXT NOT NULL,
|
||||||
|
lista_imagenes JSONB
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE posts_visited(
|
||||||
|
id_post INT,
|
||||||
|
viewed TIMESTAMP WITH TIME ZONE
|
||||||
|
);
|
5014
Documents_ref/query_sample_contact.sql
Normal file
BIN
forms_py/__pycache__/cls_change_pswd.cpython-312.pyc
Normal file
BIN
forms_py/__pycache__/cls_form_EditUser.cpython-312.pyc
Normal file
BIN
forms_py/__pycache__/cls_form_add_usr.cpython-312.pyc
Normal file
BIN
forms_py/__pycache__/cls_form_edit_user.cpython-312.pyc
Normal file
22
forms_py/cls_change_pswd.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from wtforms import StringField, PasswordField
|
||||||
|
from wtforms.validators import InputRequired, Email, Length, DataRequired
|
||||||
|
|
||||||
|
class ChangePwsd(FlaskForm):
|
||||||
|
cur_pswd = PasswordField(
|
||||||
|
'Contrseña Actual',
|
||||||
|
validators=[
|
||||||
|
DataRequired(message="La contraseña es obligatoria")
|
||||||
|
],
|
||||||
|
description="contraseña actual",
|
||||||
|
render_kw={"placeholder": "Ingresa tu contraseña"}
|
||||||
|
)
|
||||||
|
|
||||||
|
new_pswd = PasswordField(
|
||||||
|
'Nueva Contraseña',
|
||||||
|
validators=[
|
||||||
|
DataRequired(message="La nueva contraseña es obligatoria")
|
||||||
|
],
|
||||||
|
description="Contraseña nueva",
|
||||||
|
render_kw={"placeholder": "Ingresa tu nueva contraseña"}
|
||||||
|
)
|
@ -31,7 +31,7 @@ class DBContact:
|
|||||||
:raises Exception: Si ocurre un error durante la ejecución de la consulta.
|
:raises Exception: Si ocurre un error durante la ejecución de la consulta.
|
||||||
"""
|
"""
|
||||||
query = sql.SQL("""
|
query = sql.SQL("""
|
||||||
INSERT INTO contact (fecha, hora, nombre, apellido, email, num_tel, size_co, rol_contacto, industry_type, tipo_req)
|
INSERT INTO contact (full_date_time, nombre, apellido, email, estado, num_tel, size_co, rol_contacto, industry_type, tipo_req)
|
||||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
@ -65,6 +65,47 @@ class DBForma:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise RuntimeError(f"Error al verificar credenciales: {e}")
|
raise RuntimeError(f"Error al verificar credenciales: {e}")
|
||||||
|
|
||||||
|
def get_data(self, query: str, data_tuple: tuple):
|
||||||
|
try:
|
||||||
|
with self._get_connection() as conn:
|
||||||
|
with conn.cursor() as cursor:
|
||||||
|
cursor.execute(query, data_tuple)
|
||||||
|
result = cursor.fetchone()
|
||||||
|
return result
|
||||||
|
except psycopg2.DatabaseError as e:
|
||||||
|
conn.rollback() # Asegura que la conexión no quede en un estado erróneo
|
||||||
|
raise RuntimeError(f"Error al ejecutar la consulta: {e}")
|
||||||
|
except Exception as e:
|
||||||
|
raise RuntimeError(f"Error inesperado: {e}")
|
||||||
|
|
||||||
|
def get_all_data(self, query):
|
||||||
|
try:
|
||||||
|
with self._get_connection() as conn:
|
||||||
|
with conn.cursor() as cursor:
|
||||||
|
cursor.execute(query)
|
||||||
|
result = cursor.fetchall()
|
||||||
|
return result
|
||||||
|
except psycopg2.DatabaseError as e:
|
||||||
|
conn.rollback() # Asegura que la conexión no quede en un estado erróneo
|
||||||
|
raise RuntimeError(f"Error al ejecutar la consulta: {e}")
|
||||||
|
except Exception as e:
|
||||||
|
raise RuntimeError(f"Error inesperado: {e}")
|
||||||
|
|
||||||
|
def update_data(self, query: str, data_tuple: tuple) -> bool:
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with self._get_connection() as conn:
|
||||||
|
with conn.cursor() as cursor:
|
||||||
|
cursor.execute(query, data_tuple)
|
||||||
|
if cursor.rowcount == 0:
|
||||||
|
return False
|
||||||
|
conn.commit() # Confirmar explícitamente la transacción
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
conn.rollback() # Revertir en caso de error
|
||||||
|
raise RuntimeError(f"Error al actualizar la contraseña: {e}")
|
||||||
|
|
||||||
|
|
||||||
def update_pswd(self, pswd: str, email: str) -> bool:
|
def update_pswd(self, pswd: str, email: str) -> bool:
|
||||||
"""
|
"""
|
||||||
|
32
forms_py/cls_form_add_usr.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from wtforms import StringField, TextAreaField, SelectField
|
||||||
|
from wtforms.validators import InputRequired, Email, Length, Optional, DataRequired
|
||||||
|
from wtforms import HiddenField
|
||||||
|
|
||||||
|
class AddUser(FlaskForm):
|
||||||
|
id = HiddenField()
|
||||||
|
|
||||||
|
nombre = StringField(
|
||||||
|
"Nombre", validators=[InputRequired(message="Este campo es obligatorio"), Length(max=100, message="Máximo 100 caracteres")]
|
||||||
|
)
|
||||||
|
|
||||||
|
apellido = StringField(
|
||||||
|
"Apellido", validators=[InputRequired(message="Este campo es obligatorio"), Length(max=100, message="Máximo 100 caracteres")]
|
||||||
|
)
|
||||||
|
|
||||||
|
genero = SelectField("Género", choices=[
|
||||||
|
("", ""),
|
||||||
|
('M', 'Hombre'),
|
||||||
|
('F', 'Mujer')
|
||||||
|
], validators=[DataRequired(message="Debes seleccionar el género.")],)
|
||||||
|
|
||||||
|
email = StringField(
|
||||||
|
"Email", validators=[InputRequired(message="Este campo es obligatorio"), Email(message="Ingresa un email válido"), Length(max=120)]
|
||||||
|
)
|
||||||
|
|
||||||
|
isAdmin = SelectField( "Permisos de Administrador", choices=[
|
||||||
|
("", ""),
|
||||||
|
("true", "Sí"),
|
||||||
|
("false", "No")
|
||||||
|
], validators=[DataRequired(message="Debes seleccionar el rol.")],)
|
||||||
|
|
@ -16,39 +16,39 @@ class ContactForm(FlaskForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
estado = SelectField("Estado", choices=[
|
estado = SelectField("Estado", choices=[
|
||||||
("", ""),
|
("", "Selecciona tu Estado *"),
|
||||||
(1, 'Aguascalientes'),
|
('Aguascalientes', 'Aguascalientes'),
|
||||||
(2, 'Baja California'),
|
('Baja California', 'Baja California'),
|
||||||
(3, 'Baja California Sur'),
|
('Baja California Sur', 'Baja California Sur'),
|
||||||
(4, 'Campeche'),
|
('Campeche', 'Campeche'),
|
||||||
(5, 'Chiapas'),
|
('Chiapas', 'Chiapas'),
|
||||||
(6, 'Chihuahua'),
|
('Chihuahua', 'Chihuahua'),
|
||||||
(7, 'Ciudad de México'),
|
('Ciudad de México', 'Ciudad de México'),
|
||||||
(8, 'Coahuila'),
|
('Coahuila', 'Coahuila'),
|
||||||
(9, 'Colima'),
|
('Colima', 'Colima'),
|
||||||
(10, 'Durango'),
|
('Durango', 'Durango'),
|
||||||
(11, 'Estado de México'),
|
('Estado de México', 'Estado de México'),
|
||||||
(12, 'Guanajuato'),
|
('Guanajuato', 'Guanajuato'),
|
||||||
(13, 'Guerrero'),
|
('Guerrero', 'Guerrero'),
|
||||||
(14, 'Hidalgo'),
|
('Hidalgo', 'Hidalgo'),
|
||||||
(15, 'Jalisco'),
|
('Jalisco', 'Jalisco'),
|
||||||
(16, 'Michoacán'),
|
('Michoacán', 'Michoacán'),
|
||||||
(17, 'Morelos'),
|
('Morelos', 'Morelos'),
|
||||||
(18, 'Nayarit'),
|
('Nayarit', 'Nayarit'),
|
||||||
(19, 'Nuevo León'),
|
('Nuevo León', 'Nuevo León'),
|
||||||
(20, 'Oaxaca'),
|
('Oaxaca', 'Oaxaca'),
|
||||||
(21, 'Puebla'),
|
('Puebla', 'Puebla'),
|
||||||
(22, 'Querétaro'),
|
('Querétaro', 'Querétaro'),
|
||||||
(23, 'Quintana Roo'),
|
('Quintana Roo', 'Quintana Roo'),
|
||||||
(24, 'San Luis Potosí'),
|
('San Luis Potosí', 'San Luis Potosí'),
|
||||||
(25, 'Sinaloa'),
|
('Sinaloa', 'Sinaloa'),
|
||||||
(26, 'Sonora'),
|
('Sonora', 'Sonora'),
|
||||||
(27, 'Tabasco'),
|
('Tabasco', 'Tabasco'),
|
||||||
(28, 'Tamaulipas'),
|
('Tamaulipas', 'Tamaulipas'),
|
||||||
(29, 'Tlaxcala'),
|
('Tlaxcala', 'Tlaxcala'),
|
||||||
(30, 'Veracruz'),
|
('Veracruz', 'Veracruz'),
|
||||||
(31, 'Yucatán'),
|
('Yucatán', 'Yucatán'),
|
||||||
(32, 'Zacatecas')
|
('Zacatec', 'Zacatecas')
|
||||||
], validators=[DataRequired(message="Debes seleccionar un estado.")],)
|
], validators=[DataRequired(message="Debes seleccionar un estado.")],)
|
||||||
|
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ class ContactForm(FlaskForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
size_co = SelectField( "Tamaño empresa", choices=[
|
size_co = SelectField( "Tamaño empresa", choices=[
|
||||||
("", ""),
|
("", "Tamaño de la Empresa *"),
|
||||||
("1 - 10", "1 - 20"),
|
("1 - 10", "1 - 20"),
|
||||||
("10 - 50", "21 - 50"),
|
("10 - 50", "21 - 50"),
|
||||||
("50 - 100", "51 - 100"),
|
("50 - 100", "51 - 100"),
|
||||||
@ -71,52 +71,52 @@ class ContactForm(FlaskForm):
|
|||||||
|
|
||||||
|
|
||||||
rol_contacto = SelectField( "Rol desempeñado", choices=[
|
rol_contacto = SelectField( "Rol desempeñado", choices=[
|
||||||
("", ""),
|
("", "Te desempeñas en * "),
|
||||||
(1, 'Capacitaciones'),
|
('Capacitaciones', 'Capacitaciones'),
|
||||||
(2, 'Gerente de Operaciones'),
|
('Gerente de Operaciones', 'Gerente de Operaciones'),
|
||||||
(3, 'Gerente de TI'),
|
('Gerente de TI', 'Gerente de TI'),
|
||||||
(4, 'Nómina'),
|
('Nómina', 'Nómina'),
|
||||||
(5, 'Desarrollo Organizacional'),
|
('Desarrollo Organizacional', 'Desarrollo Organizacional'),
|
||||||
(6, 'Reclutamiento y selección'),
|
('Reclutamiento y selección', 'Reclutamiento y selección'),
|
||||||
(7, 'Gerencia de Recursos Humanos'),
|
('Gerencia de Recursos Humanos', 'Gerencia de Recursos Humanos'),
|
||||||
(8, 'Finanzas / Gerencia'),
|
('Finanzas / Gerencia', 'Finanzas / Gerencia'),
|
||||||
(9, 'Dueño de mi propio negocio'),
|
('Dueño de mi propio negocio', 'Dueño de mi propio negocio'),
|
||||||
(10, 'Estudiante'),
|
('Estudiante', 'Estudiante'),
|
||||||
(11, 'Otro')
|
('Otro', 'Otro')
|
||||||
], validators=[DataRequired(message="Debes selecionar tu rol.")],)
|
], validators=[DataRequired(message="Debes selecionar tu rol.")],)
|
||||||
|
|
||||||
industry_type = SelectField( "Sector", choices=[
|
industry_type = SelectField( "Sector", choices=[
|
||||||
("", ""),
|
("", "Industria a la que pertenece la empresa *"),
|
||||||
(1, 'Agricola'),
|
('Agricola', 'Agricola'),
|
||||||
(2, 'Alimentos'),
|
('Alimentos', 'Alimentos'),
|
||||||
(3, 'Automotriz'),
|
('Automotriz', 'Automotriz'),
|
||||||
(4, 'Comercio'),
|
('Comercio', 'Comercio'),
|
||||||
(5, 'Comunicaciones'),
|
('Comunicaciones', 'Comunicaciones'),
|
||||||
(6, 'Construcción'),
|
('Construcción', 'Construcción'),
|
||||||
(7, 'Consultora'),
|
('Consultora', 'Consultora'),
|
||||||
(8, 'Educación'),
|
('Educación', 'Educación'),
|
||||||
(9, 'Empresas B'),
|
('Empresas B', 'Empresas B'),
|
||||||
(10, 'Energia'),
|
('Energia', 'Energia'),
|
||||||
(11, 'Entretenimiento'),
|
('Entretenimiento', 'Entretenimiento'),
|
||||||
(12, 'Financiera'),
|
('Financiera', 'Financiera'),
|
||||||
(13, 'Fundación'),
|
('Fundación', 'Fundación'),
|
||||||
(14, 'Holding'),
|
('Holding', 'Holding'),
|
||||||
(15, 'Hoteleria'),
|
('Hoteleria', 'Hoteleria'),
|
||||||
(16, 'Legal'),
|
('Legal', 'Legal'),
|
||||||
(17, 'Logistica'),
|
('Logistica', 'Logistica'),
|
||||||
(18, 'Manufacturera'),
|
('Manufacturera', 'Manufacturera'),
|
||||||
(19, 'Marketing'),
|
('Marketing', 'Marketing'),
|
||||||
(20, 'Minera'),
|
('Minera', 'Minera'),
|
||||||
(21, 'Otra'),
|
('Otra', 'Otra'),
|
||||||
(22, 'Sector Público'),
|
('Sector Público', 'Sector Público'),
|
||||||
(23, 'Restoranes/Cafeteria'),
|
('Restoranes/Cafeteria', 'Restoranes/Cafeteria'),
|
||||||
(24, 'RRHH'),
|
('RRHH', 'RRHH'),
|
||||||
(25, 'Salud'),
|
('Salud', 'Salud'),
|
||||||
(26, 'Seguridad'),
|
('Seguridad', 'Seguridad'),
|
||||||
(27, 'Servicios'),
|
('Servicios', 'Servicios'),
|
||||||
(28, 'Tecnología'),
|
('Tecnología', 'Tecnología'),
|
||||||
(29, 'Transporte'),
|
('Transporte', 'Transporte'),
|
||||||
(30, 'Utilities (gas, agua, electricidad)')
|
('Utilities (gas, agua, electricidad)', 'Utilities (gas, agua, electricidad)')
|
||||||
], validators=[DataRequired(message="Selecione una propiedad.")],)
|
], validators=[DataRequired(message="Selecione una propiedad.")],)
|
||||||
|
|
||||||
tipo_req = TextAreaField(
|
tipo_req = TextAreaField(
|
||||||
|
@ -1,33 +1,28 @@
|
|||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import StringField, validators
|
from wtforms import StringField, PasswordField
|
||||||
from wtforms.validators import DataRequired, Email, Length
|
from wtforms.validators import InputRequired, Email, Length, DataRequired
|
||||||
|
|
||||||
class RecoverPswd(FlaskForm):
|
class RecoverPswd(FlaskForm):
|
||||||
"""
|
"""
|
||||||
Formulario para recuperación de contraseña.
|
Formulario de inicio de sesión que hereda de FlaskForm.
|
||||||
|
|
||||||
Campos:
|
Campos:
|
||||||
email (StringField): Campo para el correo electrónico del usuario con validaciones.
|
email (StringField): Campo para el correo electrónico del usuario.
|
||||||
|
password (PasswordField): Campo para la contraseña del usuario.
|
||||||
|
|
||||||
Validaciones:
|
Validaciones:
|
||||||
- Requerido (DataRequired)
|
- Email: Requerido, formato válido y longitud máxima de 120 caracteres.
|
||||||
- Formato válido de email (Email)
|
- Password: Requerido.
|
||||||
- Longitud máxima de 120 caracteres (Length)
|
|
||||||
- Sanitización automática (elimina espacios)
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
email = StringField(
|
email = StringField(
|
||||||
label="Correo Electrónico",
|
"Email",
|
||||||
description="Ingresa el email asociado a tu cuenta",
|
|
||||||
filters=[lambda x: x.strip() if x else x], # Sanitización
|
|
||||||
validators=[
|
validators=[
|
||||||
DataRequired(message="El correo electrónico es requerido"),
|
InputRequired(message="Este campo es obligatorio"),
|
||||||
Email(message="Por favor ingresa un email válido"),
|
Email(message="Ingresa un email válido"),
|
||||||
Length(max=120, message="El email no puede exceder los 120 caracteres")
|
Length(max=120, message="El email no puede exceder los 120 caracteres")
|
||||||
],
|
],
|
||||||
render_kw={
|
description="Correo electrónico registrado en la plataforma"
|
||||||
"placeholder": "ejemplo@correo.com",
|
|
||||||
"class": "form-control", # Para Bootstrap
|
|
||||||
"autocomplete": "email"
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -6,22 +6,45 @@ import string
|
|||||||
import secrets
|
import secrets
|
||||||
from flask_bcrypt import Bcrypt
|
from flask_bcrypt import Bcrypt
|
||||||
import shortuuid
|
import shortuuid
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from zoneinfo import ZoneInfo # Python 3.9+
|
||||||
|
|
||||||
bcrypt = Bcrypt()
|
bcrypt = Bcrypt()
|
||||||
|
|
||||||
|
|
||||||
v = {
|
v = {
|
||||||
'home': 'home/home.html',
|
'home': 'a_home/home.html',
|
||||||
'about-us': 'about-us/about-us.html',
|
'about-us': 'b_about-us/about-us.html',
|
||||||
'solutions': 'solutions/solutions.html',
|
'solutions': 'c_solutions/solutions.html',
|
||||||
'methodology': 'methodology/methodology.html',
|
'methodology': 'd_methodology/methodology.html',
|
||||||
'contact': 'contact/contact.html',
|
"blog": {
|
||||||
'login': 'login/login.html',
|
"all_posts": "e_blog/a_all_posts.html",
|
||||||
'usr_home': 'usr_home/usr_home.html',
|
"post": "e_blog/b_post.html",
|
||||||
'recover_pswd': 'login/recover_pswd.html'
|
},
|
||||||
|
'contact': 'f_contact/contact.html',
|
||||||
|
'login': 'g_login/login.html',
|
||||||
|
'recover_pswd': 'g_login/recover_pswd.html',
|
||||||
|
'tmp_user': {
|
||||||
|
'home': 'h_tmp_usr/a_home.html',
|
||||||
|
'txt_editor': 'h_tmp_usr/b_txt_editor.html',
|
||||||
|
'my_posts': 'h_tmp_usr/c_my_posts.html',
|
||||||
|
'read_post': 'h_tmp_usr/d_read_post.html',
|
||||||
|
'edit_post': 'h_tmp_usr/e_edit_post.html',
|
||||||
|
'change_pswd': 'h_tmp_usr/f_change_pswd.html',
|
||||||
|
'metrics': 'h_tmp_usr/g_metrics.html',
|
||||||
|
'manage_profiles': 'h_tmp_usr/h_manage_profiles.html'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
l_flash_msj = lambda title, body, typeAlert: {'title': title, 'body': body, 'typeAlert': typeAlert}
|
||||||
|
|
||||||
|
def min_read_pst(n_words: int) -> str:
|
||||||
|
# https://uapas2.bunam.unam.mx/humanidades/velocidad_lectora/
|
||||||
|
# min 250 y más 500 = 750 / 2 = ~375
|
||||||
|
t = int(round(n_words / 375, 0))
|
||||||
|
resultado = f'{t} min' if t < 60 else f'{int(t/60)} hrs'
|
||||||
|
return resultado
|
||||||
|
|
||||||
def db_conf_obj(dbEnvVarName: str) -> object:
|
def db_conf_obj(dbEnvVarName: str) -> object:
|
||||||
'''
|
'''
|
||||||
@ -92,5 +115,25 @@ def getRandomId():
|
|||||||
short_id_custom = shortuuid.uuid()
|
short_id_custom = shortuuid.uuid()
|
||||||
return short_id_custom
|
return short_id_custom
|
||||||
|
|
||||||
|
def saludo_hr():
|
||||||
|
hora = datetime.now().hour
|
||||||
|
if 5 <= hora < 12:
|
||||||
|
return "🌅 Buenos días"
|
||||||
|
elif 12 <= hora < 18:
|
||||||
|
return "🌞 Buenas tardes"
|
||||||
|
else:
|
||||||
|
return "🌙 Buenas noches"
|
||||||
|
|
||||||
|
|
||||||
|
def cur_date() -> datetime:
|
||||||
|
fecha_actual = datetime.now(ZoneInfo("America/Mexico_City"))
|
||||||
|
return fecha_actual
|
||||||
|
|
||||||
|
|
||||||
|
def get_date_n_time(timestamp: datetime) -> dict:
|
||||||
|
obj = {
|
||||||
|
"date": timestamp.strftime("%d/%m/%Y"),
|
||||||
|
"hour": timestamp.strftime("%H:%M:%S")
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
|
||||||
|
850
main.py
@ -1,55 +1,120 @@
|
|||||||
from flask_jwt_extended import JWTManager, create_access_token, jwt_required, get_jwt_identity, get_jwt
|
from flask_jwt_extended import JWTManager, create_access_token, jwt_required, get_jwt_identity, get_jwt, decode_token, verify_jwt_in_request, set_access_cookies, get_csrf_token
|
||||||
from flask_mail import Message, Mail
|
from flask_mail import Message, Mail
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from flask import Flask, render_template, redirect, url_for, flash, current_app # Añadí current_app
|
from flask import Flask, render_template, redirect, url_for, flash, current_app, request, jsonify, make_response
|
||||||
from flask import jsonify, make_response # Añade estas importaciones
|
|
||||||
from forms_py.cls_form_contact import ContactForm
|
from forms_py.cls_form_contact import ContactForm
|
||||||
|
from forms_py.cls_form_add_usr import AddUser
|
||||||
from forms_py.cls_form_login import LogIn
|
from forms_py.cls_form_login import LogIn
|
||||||
from forms_py.cls_db import DBContact
|
from forms_py.cls_db import DBContact
|
||||||
from forms_py.cls_db_usr import DBForma
|
from forms_py.cls_db_usr import DBForma
|
||||||
from forms_py.functions import db_conf_obj, generar_contrasena, hash_password, v
|
from forms_py.cls_change_pswd import ChangePwsd
|
||||||
|
from forms_py.functions import db_conf_obj, generar_contrasena, hash_password, v, l_flash_msj, saludo_hr, cur_date, min_read_pst, get_date_n_time, getRandomId
|
||||||
from forms_py.cls_recover_pswd import RecoverPswd
|
from forms_py.cls_recover_pswd import RecoverPswd
|
||||||
import os
|
import os
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta, timezone
|
||||||
from flask_bcrypt import Bcrypt
|
from flask_bcrypt import Bcrypt
|
||||||
|
# from flask_wtf.csrf import CSRFProtect
|
||||||
|
from werkzeug.utils import secure_filename
|
||||||
|
import re
|
||||||
|
import json
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
bcrypt = Bcrypt(app)
|
bcrypt = Bcrypt(app)
|
||||||
|
# csrf = CSRFProtect(app)
|
||||||
|
|
||||||
email_sender = os.getenv("email_sender")
|
email_sender = os.getenv("email_sender")
|
||||||
email_pswd = os.getenv("pswd_formha")
|
email_pswd = os.getenv("pswd_formha")
|
||||||
lst_email_to = ["davidix1991@gmail.com", "davicho1991@live.com"]
|
lst_email_to = ["davidix1991@gmail.com", "davicho1991@live.com"]
|
||||||
|
|
||||||
# Configuración de JWT (añade esto junto a tus otras configs)
|
# \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
|
||||||
app.config['JWT_SECRET_KEY'] = os.getenv('JWT_SECRET_KEY', 'super-secret-fallback-key') # Usa una clave segura en producción -> MOVER A VARIABLE DE ENTORNO
|
# INICIO CONFIGURACIÓN JWT
|
||||||
# app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(hours=1) # Token expira en 1 hora
|
app.config["JWT_SECRET_KEY"] = "k3y-$up3r-s3cret4" # Usa una clave segura en producción -> MOVER A VARIABLE DE ENTORNO
|
||||||
app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(minutes=30) # Token expira en 1 hora
|
app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(hours=1.5) # timedelta(hours=1) Token expira en 1 hora | timedelta(minutes=1) ` timedelta(seconds=60)`
|
||||||
app.config['JWT_COOKIE_SECURE'] = True # En producción debe ser True
|
# EN LOCALHOST FALSE, EN PRODUCCIÓN TRUE
|
||||||
app.config['JWT_COOKIE_CSRF_PROTECT'] = True # Recomendado para seguridad
|
app.config['JWT_COOKIE_SECURE'] = False # True en producción con HTTPS
|
||||||
app.config['JWT_TOKEN_LOCATION'] = ['cookies']
|
app.config['JWT_COOKIE_CSRF_PROTECT'] = False # True: Solo para producción, requiere HTTPS
|
||||||
# FLASK EMAIL
|
app.config['JWT_TOKEN_LOCATION'] = ['cookies'] # Ubicación donde buscar el token
|
||||||
|
app.config['JWT_ACCESS_COOKIE_NAME'] = 'access_token_cookie' # Asegura que use el mismo nombre
|
||||||
|
# app.config['JWT_ACCESS_CSRF_COOKIE_NAME'] = 'csrf_access_token'
|
||||||
|
app.config['JWT_COOKIE_SAMESITE'] = 'Lax'
|
||||||
|
|
||||||
|
# app.config['SESSION_PERMANENT'] = False
|
||||||
|
# app.config['SESSION_TYPE'] = 'filesystem' # Asegura que la sesión no se guarde en cookies
|
||||||
|
jwt = JWTManager(app)
|
||||||
|
# FINAL CONFIGURACIÓN JWT
|
||||||
|
# /////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
# \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
|
||||||
|
# INICIO FLASK EMAIL
|
||||||
app.config['MAIL_SERVER'] = 'smtp.gmail.com'
|
app.config['MAIL_SERVER'] = 'smtp.gmail.com'
|
||||||
app.config['MAIL_PORT'] = 465
|
app.config['MAIL_PORT'] = 465
|
||||||
app.config['MAIL_USE_SSL'] = True
|
app.config['MAIL_USE_SSL'] = True
|
||||||
app.config['MAIL_USERNAME'] = email_sender # email en variable de entorno
|
app.config['MAIL_USERNAME'] = email_sender # email en variable de entorno
|
||||||
app.config['MAIL_PASSWORD'] = email_pswd # contraseña en variable de entorno
|
app.config['MAIL_PASSWORD'] = email_pswd # contraseña en variable de entorno
|
||||||
app.config['SECRET_KEY'] = 'FoRmHä$2025' # Necesario para CSRF y mensajes flash -> la debo colocar en variable de entono?
|
app.config['SECRET_KEY'] = 'FoRmHä$2025' # Necesario para CSRF y mensajes flash -> la debo colocar en variable de entono?
|
||||||
|
|
||||||
|
|
||||||
mail = Mail(app)
|
mail = Mail(app)
|
||||||
|
|
||||||
jwt = JWTManager(app)
|
def send_async_email(app, msg):
|
||||||
|
with app.app_context():
|
||||||
|
mail.send(msg)
|
||||||
|
|
||||||
|
# FINAL FLASK EMAIL
|
||||||
|
# /////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
# \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
|
||||||
|
# CONFIGURACIÓN PARA GUARDAR IMÁGENES EN EL BACKEND
|
||||||
|
# Configuración para guardar imágenes
|
||||||
|
# UPLOAD_FOLDER = 'uploads/' # Asegúrate de crear esta carpeta
|
||||||
|
# ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'webp'}
|
||||||
|
# app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
|
||||||
|
# app.add_url_rule('/uploads/<filename>', endpoint='uploaded_file', view_func=send_from_directory(UPLOAD_FOLDER))
|
||||||
|
|
||||||
|
# /////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
def cur_timestamp():
|
||||||
|
return int(datetime.now(timezone.utc).timestamp())
|
||||||
|
|
||||||
|
|
||||||
jsonDbContact = db_conf_obj("forma_db")
|
jsonDbContact = db_conf_obj("forma_db")
|
||||||
dbContact = DBContact(jsonDbContact)
|
dbContact = DBContact(jsonDbContact)
|
||||||
dbUsers = DBForma(jsonDbContact)
|
dbUsers = DBForma(jsonDbContact)
|
||||||
|
|
||||||
|
|
||||||
|
# decorador para rutas protegidas en caso de que borres al vuelo a un usuario.
|
||||||
|
def validate_user_exists(f):
|
||||||
|
@wraps(f)
|
||||||
|
def decorated_function(*args, **kwargs):
|
||||||
|
user_id = get_jwt_identity()
|
||||||
|
q = "SELECT id FROM users WHERE id = %s;"
|
||||||
|
t = (user_id,)
|
||||||
|
exists = dbUsers.get_data(q, t) is not None
|
||||||
|
|
||||||
def send_async_email(app, msg):
|
if not exists:
|
||||||
with app.app_context():
|
f_mnsj = l_flash_msj('Error', 'El usuario no existe en la base de datos.', 'error')
|
||||||
mail.send(msg)
|
flash(f_mnsj)
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
return decorated_function
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ##################################################################
|
||||||
|
#
|
||||||
|
# /$$$$$$ /$$$$$$$$ /$$ /$$ /$$$$$$$$ /$$$$$$$ /$$$$$$ /$$
|
||||||
|
# /$$__ $$| $$_____/| $$$ | $$| $$_____/| $$__ $$ /$$__ $$| $$
|
||||||
|
# | $$ \__/| $$ | $$$$| $$| $$ | $$ \ $$| $$ \ $$| $$
|
||||||
|
# | $$ /$$$$| $$$$$ | $$ $$ $$| $$$$$ | $$$$$$$/| $$$$$$$$| $$
|
||||||
|
# | $$|_ $$| $$__/ | $$ $$$$| $$__/ | $$__ $$| $$__ $$| $$
|
||||||
|
# | $$ \ $$| $$ | $$\ $$$| $$ | $$ \ $$| $$ | $$| $$
|
||||||
|
# | $$$$$$/| $$$$$$$$| $$ \ $$| $$$$$$$$| $$ | $$| $$ | $$| $$$$$$$$
|
||||||
|
# \______/ |________/|__/ \__/|________/|__/ |__/|__/ |__/|________/
|
||||||
|
#
|
||||||
|
# Font Name: Big Money-ne | https://patorjk.com/software/taag/#p=testall&f=Graffiti&t=USER
|
||||||
|
# ##################################################################
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def home():
|
def home():
|
||||||
@ -67,47 +132,147 @@ def solutions():
|
|||||||
def methodology():
|
def methodology():
|
||||||
return render_template(v['methodology'], active_page='methodology')
|
return render_template(v['methodology'], active_page='methodology')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/blog')
|
||||||
|
def blog():
|
||||||
|
|
||||||
|
# if any(x in search for x in ["'", '"', " OR ", "--", ";", "1=1"]):
|
||||||
|
# app.logger.warning(f"Intento de SQL injection detectado: {search}")
|
||||||
|
# 🛑 IMPORTANTE: Este bloque acepta input del usuario y necesita sanitización adecuada.
|
||||||
|
# TODO: Reemplazar concatenación de strings por parámetros SQL usando psycopg2.sql.SQL y placeholders (%s)
|
||||||
|
# Ejemplo de ataque detectado: ' OR '1'='1
|
||||||
|
# Aunque no ejecuta el ataque, sí lanza error → posible vector de DoS
|
||||||
|
|
||||||
|
|
||||||
|
# Parámetros
|
||||||
|
page = request.args.get("page", 1, type=int)
|
||||||
|
search = request.args.get("q", "").strip()
|
||||||
|
per_page = 9
|
||||||
|
offset = (page - 1) * per_page
|
||||||
|
|
||||||
|
# Armado de condiciones SQL para búsqueda
|
||||||
|
search_filter = ""
|
||||||
|
if search:
|
||||||
|
like = f"'%{search}%'"
|
||||||
|
search_filter = f"""
|
||||||
|
WHERE
|
||||||
|
LOWER(p.title) LIKE LOWER({like}) OR
|
||||||
|
LOWER(p.body_no_img) LIKE LOWER({like}) OR
|
||||||
|
LOWER(u.nombre || ' ' || u.apellido) LIKE LOWER({like})
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Conteo total
|
||||||
|
count_query = f"""
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM posts p
|
||||||
|
INNER JOIN users u ON u.id = p.id_usr
|
||||||
|
{search_filter};
|
||||||
|
"""
|
||||||
|
total_posts = dbUsers.get_all_data(count_query)[0][0]
|
||||||
|
total_pages = (total_posts + per_page - 1) // per_page
|
||||||
|
|
||||||
|
# Consulta con paginación
|
||||||
|
q = fr"""
|
||||||
|
SELECT
|
||||||
|
p.id,
|
||||||
|
u.nombre,
|
||||||
|
u.apellido,
|
||||||
|
TO_CHAR(p.created_at, 'DD/MM/YYYY HH24:MI'),
|
||||||
|
TO_CHAR(p.updated_at, 'DD/MM/YYYY HH24:MI'),
|
||||||
|
p.title,
|
||||||
|
LEFT(p.body_no_img, 180) AS preview,
|
||||||
|
p.lista_imagenes->0 AS primera_imagen,
|
||||||
|
array_length(regexp_split_to_array(TRIM(body_no_img), '\s+'), 1) / 375 as n_words
|
||||||
|
FROM
|
||||||
|
posts p
|
||||||
|
INNER JOIN
|
||||||
|
users u ON u.id = p.id_usr
|
||||||
|
{search_filter}
|
||||||
|
ORDER BY
|
||||||
|
p.created_at DESC
|
||||||
|
LIMIT {per_page} OFFSET {offset};
|
||||||
|
"""
|
||||||
|
data = dbUsers.get_all_data(q)
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
v['blog']['all_posts'],
|
||||||
|
active_page='blog',
|
||||||
|
data=data,
|
||||||
|
current_page=page,
|
||||||
|
total_pages=total_pages,
|
||||||
|
search=search # pasamos el término de búsqueda a la plantilla
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/blog/<int:post_id>')
|
||||||
|
def blog_post(post_id):
|
||||||
|
q_visited = "INSERT INTO posts_visited (id_post, viewed ) VALUES ( %s, %s);"
|
||||||
|
t_visited = (post_id, cur_date())
|
||||||
|
dbUsers.update_data(q_visited, t_visited)
|
||||||
|
# Obtener el post
|
||||||
|
q = fr"""
|
||||||
|
SELECT
|
||||||
|
u.nombre,
|
||||||
|
u.apellido,
|
||||||
|
TO_CHAR(p.created_at, 'DD/MM/YYYY HH24:MI') AS fecha_creada,
|
||||||
|
TO_CHAR(p.updated_at, 'DD/MM/YYYY HH24:MI') AS fecha_updated,
|
||||||
|
array_length(regexp_split_to_array(TRIM(p.body_no_img), '\s+'), 1) / 375 as read_time_min,
|
||||||
|
p.title,
|
||||||
|
p.body
|
||||||
|
FROM
|
||||||
|
posts p
|
||||||
|
INNER JOIN
|
||||||
|
users u
|
||||||
|
ON
|
||||||
|
p.id_usr = u.id
|
||||||
|
WHERE
|
||||||
|
p.id = %s;
|
||||||
|
"""
|
||||||
|
t = (post_id,)
|
||||||
|
data = dbUsers.get_data(q, t)
|
||||||
|
|
||||||
|
return render_template(v['blog']['post'], data=data)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/contact", methods=['GET', 'POST'])
|
@app.route("/contact", methods=['GET', 'POST'])
|
||||||
def contact():
|
def contact():
|
||||||
form = ContactForm()
|
form = ContactForm()
|
||||||
|
# print(cur_date())
|
||||||
|
# get_date_n_time
|
||||||
|
|
||||||
if form.validate_on_submit(): # Corregí "validate_on_sumbit" a "validate_on_submit"
|
if form.validate_on_submit():
|
||||||
cur_date = datetime.now().strftime("%d/%m/%Y")
|
|
||||||
cur_hour = datetime.now().strftime("%H:%M")
|
|
||||||
|
|
||||||
# Procesar datos del formulario
|
# Procesar datos del formulario
|
||||||
flash('¡Gracias por contactarnos! Te responderemos pronto.', 'success')
|
f_mnsj = l_flash_msj('Contacto', '¡Gracias por contactarnos! Te responderemos pronto. ☺️', 'success' )
|
||||||
data = (cur_date, cur_hour, form.nombre.data, form.apellido.data,
|
flash(f_mnsj)
|
||||||
form.email.data, form.num_tel.data, form.size_co.data,
|
|
||||||
form.rol_contacto.data, form.industry_type.data, form.tipo_req.data)
|
|
||||||
|
c_date = cur_date()
|
||||||
|
|
||||||
|
obj_datetime = get_date_n_time(c_date)
|
||||||
|
hora = obj_datetime['hour']
|
||||||
|
fecha = obj_datetime['date']
|
||||||
|
|
||||||
|
data = ( c_date, form.nombre.data, form.apellido.data, form.email.data, form.estado.data, form.num_tel.data, form.size_co.data, form.rol_contacto.data, form.industry_type.data, form.tipo_req.data )
|
||||||
|
|
||||||
|
# Guardar datos en la base de datos
|
||||||
dbContact.carga_contact(data)
|
dbContact.carga_contact(data)
|
||||||
|
|
||||||
# Configurar y enviar email asíncrono
|
# Configurar y enviar email asíncrono
|
||||||
msg = Message(
|
msg = Message( "Subject, una persona busca asesoria", sender=email_sender, recipients=lst_email_to )
|
||||||
"Subject, una persona busca asesoria", sender=email_sender, recipients=lst_email_to
|
|
||||||
)
|
|
||||||
|
|
||||||
msg.html ="""
|
msg.html ="""
|
||||||
<h1>Nuevo contacto recibido</h1>
|
<h1>Nuevo contacto recibido</h1>
|
||||||
<p><strong>Nombre:</strong> {} {}</p>
|
<p><trong>Fecha: </strong> {}</p><p><strong>Hora:</strong> {}</p> <p><strong>Nombre:</strong> {} {}</p> <p><strong>Email:</strong> {}</p> <p><strong>Teléfono:</strong> {}</p> <p><strong>Mensaje:</strong> {}</p> <a href="http://127.0.0.1:8089/login" target='_blank'>Iniciar Sesión</a>
|
||||||
<p><strong>Email:</strong> {}</p>
|
""".format(fecha, hora, form.nombre.data, form.apellido.data, form.email.data, form.num_tel.data, form.tipo_req.data)
|
||||||
<p><strong>Teléfono:</strong> {}</p>
|
|
||||||
<p><strong>Mensaje:</strong> {}</p>
|
|
||||||
""".format(form.nombre.data, form.apellido.data, form.email.data,
|
|
||||||
form.num_tel.data, form.tipo_req.data)
|
|
||||||
|
|
||||||
# Enviar en segundo plano
|
# Enviar en segundo plano
|
||||||
thr = Thread(
|
thr = Thread( target=send_async_email, args=(current_app._get_current_object(), msg) )
|
||||||
target=send_async_email,
|
|
||||||
args=(current_app._get_current_object(), msg)
|
|
||||||
)
|
|
||||||
thr.start()
|
thr.start()
|
||||||
|
|
||||||
return redirect(url_for('contact'))
|
return redirect(url_for('contact'))
|
||||||
|
|
||||||
return render_template(v['contact'], form=form, active_page='contact')
|
return render_template(v['contact'], form=form, active_page='contact')
|
||||||
|
|
||||||
|
|
||||||
@app.route("/login", methods=['GET', 'POST'])
|
@app.route("/login", methods=['GET', 'POST'])
|
||||||
def login():
|
def login():
|
||||||
form = LogIn()
|
form = LogIn()
|
||||||
@ -117,25 +282,30 @@ def login():
|
|||||||
res_pswd_server = dbUsers.login((f_email))
|
res_pswd_server = dbUsers.login((f_email))
|
||||||
|
|
||||||
if res_pswd_server is None:
|
if res_pswd_server is None:
|
||||||
flash('Usuario no registrado en la db', 'error')
|
f_mnsj = l_flash_msj('Información', 'No se cueta con información del usuario', 'warning')
|
||||||
|
flash(f_mnsj)
|
||||||
return redirect(url_for('login'))
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
res_pswd_server = res_pswd_server[0]
|
res_pswd_server = res_pswd_server[0]
|
||||||
if bcrypt.check_password_hash(res_pswd_server, f_pswd):
|
if bcrypt.check_password_hash(res_pswd_server, f_pswd):
|
||||||
|
|
||||||
id_user = dbUsers.get_id(f_email)[0]
|
id_user = dbUsers.get_id(f_email)[0]
|
||||||
|
is_admin = dbUsers.get_data('SELECT is_admin FROM users WHERE id = %s;', (id_user,))[0]
|
||||||
|
|
||||||
# Crear token JWT
|
# Crear token JWT: access_token = create_access_token(identity=id_user)
|
||||||
access_token = create_access_token(identity=id_user)
|
# access_token = create_access_token( identity=id_user )
|
||||||
|
access_token = create_access_token( identity=id_user, additional_claims={"is_admin": is_admin} )
|
||||||
|
|
||||||
# Redirigir a usr_home
|
# Redirigir a usr_home o a Admin depende
|
||||||
response = make_response(redirect(url_for('usr_home')))
|
# din_home = 'admin_home' if is_admin else 'user_home'
|
||||||
response.set_cookie('access_token_cookie', access_token, httponly=True, secure=True, samesite='Lax' )
|
|
||||||
flash('Inicio de sesión exitoso', 'success')
|
response = make_response(redirect(url_for("user_home")))
|
||||||
|
|
||||||
|
set_access_cookies(response, access_token)
|
||||||
return response
|
return response
|
||||||
else:
|
else:
|
||||||
flash('Credenciales incorrectas', 'error')
|
f_mnsj = l_flash_msj('Credenciales', 'Verificar usuario y/o contraseña.', 'error')
|
||||||
|
flash(f_mnsj)
|
||||||
return render_template(v['login'], form=form, active_page='login')
|
return render_template(v['login'], form=form, active_page='login')
|
||||||
|
|
||||||
|
|
||||||
@ -147,7 +317,8 @@ def recover_pswd():
|
|||||||
emailPswdReco = dbUsers.reset_pswd(f_email)
|
emailPswdReco = dbUsers.reset_pswd(f_email)
|
||||||
|
|
||||||
if emailPswdReco is None:
|
if emailPswdReco is None:
|
||||||
flash('Email no válido', 'error')
|
f_mnsj = l_flash_msj("Error", "Verificar el correo electrónico ingresado.", "error")
|
||||||
|
flash(f_mnsj)
|
||||||
return render_template(v['recover_pswd'], form=form, active_page='login')
|
return render_template(v['recover_pswd'], form=form, active_page='login')
|
||||||
|
|
||||||
emailPswdReco = emailPswdReco[0]
|
emailPswdReco = emailPswdReco[0]
|
||||||
@ -177,54 +348,591 @@ def recover_pswd():
|
|||||||
args=(current_app._get_current_object(), msg)
|
args=(current_app._get_current_object(), msg)
|
||||||
)
|
)
|
||||||
thr.start()
|
thr.start()
|
||||||
|
f_mnsj = l_flash_msj("Éxito", "Se ha enviado una contraseña temporal a tu correo electrónico.", "success")
|
||||||
flash("Se ha enviado una contraseña temporal a tu correo electrónico", "success")
|
flash(f_mnsj)
|
||||||
return redirect(url_for('login')) # Redirige en lugar de renderizar
|
return redirect(url_for('login')) # Redirige en lugar de renderizar
|
||||||
|
|
||||||
return render_template(v['recover_pswd'], form=form, active_page='login')
|
return render_template(v['recover_pswd'], form=form, active_page='login')
|
||||||
|
|
||||||
|
|
||||||
|
# #################################################################################################################################
|
||||||
|
#
|
||||||
|
# $$\ $$\ $$$$$$\ $$$$$$$$\ $$$$$$$\ $$$$$$\ $$$$$$$\ $$\ $$\ $$$$$$\ $$\ $$\
|
||||||
|
# $$ | $$ |$$ __$$\ $$ _____|$$ __$$\ $$ __$$\ $$ __$$\ $$$\ $$$ |\_$$ _|$$$\ $$ |
|
||||||
|
# $$ | $$ |$$ / \__|$$ | $$ | $$ | $$ / $$ |$$ | $$ |$$$$\ $$$$ | $$ | $$$$\ $$ |
|
||||||
|
# $$ | $$ |\$$$$$$\ $$$$$\ $$$$$$$ | $$$$$$\ $$$$$$$$ |$$ | $$ |$$\$$\$$ $$ | $$ | $$ $$\$$ |
|
||||||
|
# $$ | $$ | \____$$\ $$ __| $$ __$$< \______| $$ __$$ |$$ | $$ |$$ \$$$ $$ | $$ | $$ \$$$$ |
|
||||||
|
# $$ | $$ |$$\ $$ |$$ | $$ | $$ | $$ | $$ |$$ | $$ |$$ |\$ /$$ | $$ | $$ |\$$$ |
|
||||||
|
# \$$$$$$ |\$$$$$$ |$$$$$$$$\ $$ | $$ | $$ | $$ |$$$$$$$ |$$ | \_/ $$ |$$$$$$\ $$ | \$$ |
|
||||||
|
# \______/ \______/ \________|\__| \__| \__| \__|\_______/ \__| \__|\______|\__| \__|
|
||||||
|
#
|
||||||
|
# Font Name: Big Money-ne | https://patorjk.com/software/taag/#p=testall&f=Graffiti&t=USER
|
||||||
|
#
|
||||||
|
# #################################################################################################################################
|
||||||
|
|
||||||
|
@app.context_processor
|
||||||
|
@jwt_required(optional=True)
|
||||||
|
def inject_user_role():
|
||||||
|
try:
|
||||||
|
token_data = get_jwt()
|
||||||
|
return {'is_admin': token_data.get('is_admin', False)}
|
||||||
|
except Exception:
|
||||||
|
return {'is_admin': False}
|
||||||
|
|
||||||
|
|
||||||
|
def admin_required(view_func):
|
||||||
|
@wraps(view_func)
|
||||||
|
def wrapped_view(*args, **kwargs):
|
||||||
|
token_data = get_jwt()
|
||||||
|
is_admin = token_data.get('is_admin', False)
|
||||||
|
|
||||||
|
if not is_admin:
|
||||||
|
f_mnsj = l_flash_msj('Error', 'No tienes permisos para acceder a esta sección.', 'error')
|
||||||
|
flash(f_mnsj)
|
||||||
|
return redirect(url_for('user_home'))
|
||||||
|
|
||||||
|
return view_func(*args, **kwargs)
|
||||||
|
return wrapped_view
|
||||||
|
|
||||||
|
|
||||||
@app.route('/user/home')
|
@app.route('/user/home')
|
||||||
@jwt_required() # Protege esta ruta
|
@jwt_required()
|
||||||
def usr_home():
|
@validate_user_exists
|
||||||
current_user = get_jwt_identity() # Obtiene el identity (normalmente el email)
|
def user_home():
|
||||||
token_data = get_jwt() # Obtiene TODOS los datos del token decodificado
|
# get_jwt_identity()
|
||||||
|
# todo el token
|
||||||
# print("Token completo:", token_data)
|
token_data = get_jwt()
|
||||||
# print("Usuario:", current_user)
|
is_admin = token_data.get('is_admin', False)
|
||||||
|
# timestamp expiración de la sesión
|
||||||
|
exp = token_data['exp']
|
||||||
|
|
||||||
|
|
||||||
return render_template(v['usr_home'], current_user=current_user, token_data=token_data)
|
|
||||||
|
# id del usuario activo:
|
||||||
|
usr_id = token_data['sub']
|
||||||
|
|
||||||
|
q = "SELECT nombre, genero, lst_conn FROM users WHERE id = %s;"
|
||||||
|
data_saludo = dbUsers.get_data(q, (usr_id,))
|
||||||
|
nombre, gender, lst_conn = data_saludo
|
||||||
|
|
||||||
|
lst_conn = '' if lst_conn is None else get_date_n_time(lst_conn)['date']
|
||||||
|
current_date = get_date_n_time(cur_date())['date']
|
||||||
|
|
||||||
|
f_mnsj = None
|
||||||
|
|
||||||
|
if lst_conn != current_date:
|
||||||
|
# q = "UPDATE users SET lst_conn = %s WHERE id = %s;"
|
||||||
|
# d = (cur_date(), usr_id)
|
||||||
|
strGreet = 'Bienvenido' if gender == 'M' else 'Bienvenida'
|
||||||
|
f_mnsj = l_flash_msj(f'{saludo_hr()}', f'{strGreet} {nombre}', 'success')
|
||||||
|
# dbUsers.update_data(q, d)
|
||||||
|
flash(f_mnsj)
|
||||||
|
|
||||||
|
q = "UPDATE users SET lst_conn = %s WHERE id = %s;"
|
||||||
|
d = (cur_date(), usr_id)
|
||||||
|
dbUsers.update_data(q, d)
|
||||||
|
|
||||||
|
|
||||||
|
q_contact = """
|
||||||
|
SELECT
|
||||||
|
ID, TO_CHAR(FULL_DATE_TIME, 'DD/MM/YYYY') AS FECHA_FORMATEADA, NOMBRE, APELLIDO, ESTADO, SIZE_CO, ROL_CONTACTO, INDUSTRY_TYPE, STATUS
|
||||||
|
FROM
|
||||||
|
CONTACT
|
||||||
|
WHERE
|
||||||
|
STATUS <> 'Archivado'
|
||||||
|
OR STATUS IS NULL
|
||||||
|
ORDER BY
|
||||||
|
FULL_DATE_TIME DESC;
|
||||||
|
"""
|
||||||
|
data_contact = dbUsers.get_all_data(q_contact)
|
||||||
|
|
||||||
|
# return render_template(v['tmp_user']['home'], token_data=token_data, f_mnsj=f_mnsj, nombre=nombre, exp=exp, data_contact=data_contact)
|
||||||
|
return render_template(v['tmp_user']['home'], f_mnsj=f_mnsj, nombre=nombre, exp=exp, data_contact=data_contact)
|
||||||
|
|
||||||
|
@app.route('/user/manage-record', methods=['POST'])
|
||||||
|
@jwt_required()
|
||||||
|
@validate_user_exists
|
||||||
|
def manage_record():
|
||||||
|
# print(request.get_json())
|
||||||
|
# print("Cookies recibida:", request.cookies)
|
||||||
|
# token = request.cookies.get('access_token_cookie')
|
||||||
|
# decoded_token = decode_token(token)
|
||||||
|
data = request.get_json()
|
||||||
|
id = data['id']
|
||||||
|
valor = data['value']
|
||||||
|
|
||||||
|
q = 'UPDATE contact SET status = %s WHERE id = %s;'
|
||||||
|
dbUsers.update_data(q, (valor, id))
|
||||||
|
|
||||||
|
return jsonify({"type": "success"})
|
||||||
|
|
||||||
|
@app.route('/user/get-contact-data', methods=['POST'])
|
||||||
|
@jwt_required()
|
||||||
|
@validate_user_exists
|
||||||
|
def get_contact_data():
|
||||||
|
data = request.get_json()
|
||||||
|
id = data['id']
|
||||||
|
q = "SELECT TO_CHAR(FULL_DATE_TIME, 'DD/MM/YYYY') as fecha, TO_CHAR(FULL_DATE_TIME, 'HH24:MI') as hora, nombre, apellido, email, estado, num_tel, size_co, rol_contacto, industry_type, tipo_req, status FROM contact WHERE id = %s;"
|
||||||
|
t = (id,)
|
||||||
|
dbData = dbUsers.get_data(q, t)
|
||||||
|
return jsonify({"data": dbData})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/user/download-db', methods=['GET'])
|
||||||
|
@jwt_required()
|
||||||
|
@validate_user_exists
|
||||||
|
@admin_required
|
||||||
|
def download_db():
|
||||||
|
q = """
|
||||||
|
SELECT
|
||||||
|
id, TO_CHAR(FULL_DATE_TIME, 'DD/MM/YYYY') as fecha, TO_CHAR(FULL_DATE_TIME, 'HH24:MI') as hora, nombre, apellido, email, estado, num_tel, size_co, rol_contacto, industry_type, tipo_req, status
|
||||||
|
FROM
|
||||||
|
contact;
|
||||||
|
"""
|
||||||
|
dbData = dbUsers.get_all_data(q)
|
||||||
|
return jsonify({"data": dbData})
|
||||||
|
|
||||||
|
@app.route('/user/txt-editor')
|
||||||
|
@jwt_required()
|
||||||
|
@validate_user_exists
|
||||||
|
def user_txteditor():
|
||||||
|
template_name = v['tmp_user'].get('txt_editor')
|
||||||
|
return render_template(template_name)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/user/save-post', methods=['POST'])
|
||||||
|
@jwt_required()
|
||||||
|
@validate_user_exists
|
||||||
|
def save_post():
|
||||||
|
id_usr = get_jwt()['sub']
|
||||||
|
data = request.get_json()
|
||||||
|
title = data['title']
|
||||||
|
body = data['body']
|
||||||
|
|
||||||
|
soup = BeautifulSoup(body, 'html.parser')
|
||||||
|
body_no_img = soup.get_text(separator=' ', strip=True)
|
||||||
|
etiquetas_img = re.findall(r'<img[^>]+src=["\'](.*?)["\']', body)
|
||||||
|
imagenes_json = json.dumps(etiquetas_img) if etiquetas_img else None
|
||||||
|
|
||||||
|
q = None
|
||||||
|
t = None
|
||||||
|
if "id" in data:
|
||||||
|
q = 'UPDATE posts SET updated_at = %s, title = %s, body = %s, body_no_img = %s, lista_imagenes = %s::jsonb WHERE id = %s AND id_usr = %s;'
|
||||||
|
t = (cur_date(), title, body, body_no_img, imagenes_json, data['id'], id_usr)
|
||||||
|
else:
|
||||||
|
q = "INSERT INTO posts (id_usr, created_at, title, body, body_no_img, lista_imagenes) VALUES (%s, %s, %s, %s, %s, %s::jsonb);"
|
||||||
|
t = (id_usr, cur_date(), title, body, body_no_img, imagenes_json)
|
||||||
|
|
||||||
|
try:
|
||||||
|
dbUsers.update_data(q, t)
|
||||||
|
return jsonify({
|
||||||
|
"status": "success",
|
||||||
|
"title_post": title,
|
||||||
|
"redirect_url": url_for('my_posts') # o usa _external=True si se necesita URL completa
|
||||||
|
# url_for('my_posts', _external=True)
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"status": "error", "message": str(e)}), 500
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/user/my-posts')
|
||||||
|
@jwt_required()
|
||||||
|
@validate_user_exists
|
||||||
|
def my_posts():
|
||||||
|
id_usr = get_jwt()['sub']
|
||||||
|
page = int(request.args.get("page", 1))
|
||||||
|
per_page = 8 # Número de tarjetas por página
|
||||||
|
|
||||||
|
# Obtener todos los posts del usuario (puedes optimizar esto con paginación SQL real después)
|
||||||
|
q_all = fr"""
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
TO_CHAR(created_at, 'DD/MM/YYYY HH24:MI') as fecha_creada,
|
||||||
|
TO_CHAR(updated_at, 'DD/MM/YYYY HH24:MI') as fecha_updated,
|
||||||
|
title,
|
||||||
|
LEFT(body_no_img, 180),
|
||||||
|
lista_imagenes->0,
|
||||||
|
array_length(regexp_split_to_array(TRIM(body_no_img), '\s+'), 1) / 375 as n_words
|
||||||
|
FROM
|
||||||
|
posts
|
||||||
|
WHERE
|
||||||
|
id_usr = '{id_usr}'
|
||||||
|
ORDER BY
|
||||||
|
COALESCE(updated_at, created_at) DESC,
|
||||||
|
created_at DESC;
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_all = dbUsers.get_all_data(q_all)
|
||||||
|
total_posts = len(data_all)
|
||||||
|
total_pages = (total_posts + per_page - 1) // per_page
|
||||||
|
|
||||||
|
start = (page - 1) * per_page
|
||||||
|
end = start + per_page
|
||||||
|
data = data_all[start:end]
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
v['tmp_user']['my_posts'],
|
||||||
|
data=data,
|
||||||
|
current_page=page,
|
||||||
|
total_pages=total_pages
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/user/del-post', methods=['POST'])
|
||||||
|
@jwt_required()
|
||||||
|
@validate_user_exists
|
||||||
|
def del_post():
|
||||||
|
data = request.get_json()
|
||||||
|
t = (data['id'],)
|
||||||
|
q = 'DELETE FROM posts WHERE id = %s;'
|
||||||
|
dbUsers.update_data(q, t)
|
||||||
|
res = {'ok': True, 'message': f'El elemento se eliminó, pendiente manejar el error en el frontend', "id": t}
|
||||||
|
return jsonify(res), 200
|
||||||
|
|
||||||
|
@app.route('/user/<int:post_id>')
|
||||||
|
@jwt_required()
|
||||||
|
@validate_user_exists
|
||||||
|
def post(post_id):
|
||||||
|
# Obtener el post
|
||||||
|
usr_id = get_jwt()['sub']
|
||||||
|
q = "select TO_CHAR(created_at, 'DD/MM/YYYY HH24:MI') as fecha_creada, TO_CHAR(updated_at, 'DD/MM/YYYY HH24:MI') as fecha_updated, title, body from posts where id_usr = %s AND id = %s;"
|
||||||
|
t = (usr_id, post_id )
|
||||||
|
data = dbUsers.get_data(q, t)
|
||||||
|
q_nw = r"SELECT array_length(regexp_split_to_array(TRIM(body_no_img), '\s+'), 1) FROM posts WHERE id_usr = %s AND id = %s;"
|
||||||
|
n_words = dbUsers.get_data(q_nw, t)[0]
|
||||||
|
time_read = min_read_pst(n_words)
|
||||||
|
return render_template(v['tmp_user']['read_post'], data=data, time_read=time_read, post_id=post_id)
|
||||||
|
|
||||||
|
@app.route('/user/edit-post/<int:id_post>')
|
||||||
|
@jwt_required()
|
||||||
|
@validate_user_exists
|
||||||
|
def edit_post(id_post):
|
||||||
|
q = 'SELECT title, body FROM posts WHERE id = %s;'
|
||||||
|
t = (id_post,)
|
||||||
|
data = dbUsers.get_data(q, t)
|
||||||
|
return render_template(v['tmp_user']['edit_post'], data=data, id_post=id_post)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/user/change-pswd', methods=['GET', 'POST'])
|
||||||
|
@jwt_required()
|
||||||
|
@validate_user_exists
|
||||||
|
def change_pswd():
|
||||||
|
form = ChangePwsd()
|
||||||
|
if form.validate_on_submit():
|
||||||
|
f_old_pswd = form.cur_pswd.data
|
||||||
|
f_new_pswd = form.new_pswd.data
|
||||||
|
usr_id = get_jwt()['sub']
|
||||||
|
q = "SELECT pswd FROM users WHERE id = %s;"
|
||||||
|
t = (usr_id,)
|
||||||
|
data = dbUsers.get_data(q, t)[0]
|
||||||
|
|
||||||
|
if bcrypt.check_password_hash(data, f_old_pswd):
|
||||||
|
new_hash_pswd = hash_password(f_new_pswd)
|
||||||
|
q = "UPDATE users SET pswd = %s WHERE id = %s;"
|
||||||
|
t = (new_hash_pswd, usr_id)
|
||||||
|
dbUsers.update_data(q, t)
|
||||||
|
|
||||||
|
f_mnsj = l_flash_msj('Éxito', 'La contraseña ha sido actualizada.', 'success')
|
||||||
|
flash(f_mnsj)
|
||||||
|
|
||||||
|
response = make_response(redirect(url_for('login')))
|
||||||
|
response.delete_cookie('access_token_cookie')
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
f_mnsj = l_flash_msj('Error', 'La contraseña a actualizar es incorrecta.', 'error')
|
||||||
|
flash(f_mnsj)
|
||||||
|
|
||||||
|
return render_template(v['tmp_user']['change_pswd'], active_page='change_pswd', form=form)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/user/metrics')
|
||||||
|
@jwt_required()
|
||||||
|
@validate_user_exists
|
||||||
|
@admin_required
|
||||||
|
def metrics():
|
||||||
|
return render_template(v['tmp_user']['metrics'], active_page='metrics')
|
||||||
|
|
||||||
|
@app.route('/user/metrics/data')
|
||||||
|
@jwt_required()
|
||||||
|
@validate_user_exists
|
||||||
|
@admin_required
|
||||||
|
def data_metrics():
|
||||||
|
q_contact = r"""
|
||||||
|
SELECT
|
||||||
|
CASE EXTRACT(MONTH FROM full_date_time AT TIME ZONE 'America/Mexico_City')
|
||||||
|
WHEN 1 THEN 'Enero'
|
||||||
|
WHEN 2 THEN 'Febrero'
|
||||||
|
WHEN 3 THEN 'Marzo'
|
||||||
|
WHEN 4 THEN 'Abril'
|
||||||
|
WHEN 5 THEN 'Mayo'
|
||||||
|
WHEN 6 THEN 'Junio'
|
||||||
|
WHEN 7 THEN 'Julio'
|
||||||
|
WHEN 8 THEN 'Agosto'
|
||||||
|
WHEN 9 THEN 'Septiembre'
|
||||||
|
WHEN 10 THEN 'Octubre'
|
||||||
|
WHEN 11 THEN 'Noviembre'
|
||||||
|
WHEN 12 THEN 'Diciembre'
|
||||||
|
END AS mes,
|
||||||
|
COUNT(*) AS cantidad_registros,
|
||||||
|
ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER (), 1) AS porcentaje
|
||||||
|
FROM
|
||||||
|
contact
|
||||||
|
GROUP BY
|
||||||
|
EXTRACT(MONTH FROM full_date_time AT TIME ZONE 'America/Mexico_City')
|
||||||
|
ORDER BY
|
||||||
|
EXTRACT(MONTH FROM full_date_time AT TIME ZONE 'America/Mexico_City');
|
||||||
|
"""
|
||||||
|
q_count_state = "SELECT estado, COUNT(estado) AS conteo_edo, ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER (), 1) AS porcentaje FROM contact GROUP BY estado ORDER BY conteo_edo DESC;"
|
||||||
|
q_size_co = "SELECT size_co, COUNT(size_co) AS conteo_size FROM contact GROUP BY size_co ORDER BY conteo_size DESC;"
|
||||||
|
q_rol_contact = """
|
||||||
|
SELECT
|
||||||
|
rol_contacto,
|
||||||
|
COUNT(*) AS conteo,
|
||||||
|
ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER (), 1) AS porcentaje
|
||||||
|
FROM contact
|
||||||
|
GROUP BY rol_contacto
|
||||||
|
ORDER BY conteo DESC;
|
||||||
|
"""
|
||||||
|
|
||||||
|
q_industry_type = """
|
||||||
|
SELECT
|
||||||
|
industry_type,
|
||||||
|
COUNT(industry_type) AS conteo,
|
||||||
|
ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER (), 1) AS porcentaje
|
||||||
|
FROM
|
||||||
|
contact
|
||||||
|
GROUP BY
|
||||||
|
industry_type
|
||||||
|
ORDER BY
|
||||||
|
conteo DESC;
|
||||||
|
"""
|
||||||
|
|
||||||
|
q_group_status = """
|
||||||
|
SELECT
|
||||||
|
COALESCE(status, 'Sin Estatus') AS status,
|
||||||
|
COUNT(*) AS conteo,
|
||||||
|
ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER (), 1) AS porcentaje
|
||||||
|
FROM
|
||||||
|
contact
|
||||||
|
GROUP BY
|
||||||
|
COALESCE(status, 'Sin Estatus')
|
||||||
|
ORDER BY
|
||||||
|
conteo;
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_contact = {
|
||||||
|
"count_monthly": dbUsers.get_all_data(q_contact),
|
||||||
|
"count_state": dbUsers.get_all_data(q_count_state),
|
||||||
|
"size_co": dbUsers.get_all_data(q_size_co),
|
||||||
|
"rol_contact": dbUsers.get_all_data(q_rol_contact),
|
||||||
|
"industry_type": dbUsers.get_all_data(q_industry_type),
|
||||||
|
"group_status": dbUsers.get_all_data(q_group_status)
|
||||||
|
}
|
||||||
|
return jsonify(data_contact)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/user/manage-profiles', methods=['GET', 'POST'])
|
||||||
|
@jwt_required()
|
||||||
|
@validate_user_exists
|
||||||
|
@admin_required
|
||||||
|
def manage_profiles():
|
||||||
|
form = AddUser()
|
||||||
|
|
||||||
|
id_usr = get_jwt()['sub']
|
||||||
|
|
||||||
|
q_all_users = """
|
||||||
|
SELECT
|
||||||
|
u.id, u.nombre, u.apellido, u.email, COALESCE(TO_CHAR(u.lst_conn, 'DD/MM/YYYY HH24:MI'), 'Sin conexión') AS ultima_conexion,
|
||||||
|
CASE
|
||||||
|
WHEN u.is_admin = true THEN 'Sí'
|
||||||
|
WHEN u.is_admin = false THEN 'No'
|
||||||
|
END AS admin,
|
||||||
|
COALESCE(p.conteo, 0) AS conteo
|
||||||
|
FROM users u
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT id_usr, COUNT(*) AS conteo
|
||||||
|
FROM posts
|
||||||
|
GROUP BY id_usr
|
||||||
|
) p ON u.id = p.id_usr
|
||||||
|
ORDER BY conteo DESC;
|
||||||
|
"""
|
||||||
|
data_all_users = dbUsers.get_all_data(q_all_users)
|
||||||
|
|
||||||
|
if form.validate_on_submit():
|
||||||
|
f_nombre = f'{form.nombre.data}'.title().strip()
|
||||||
|
f_apellido = f'{form.apellido.data}'.title().strip()
|
||||||
|
f_genero = form.genero.data
|
||||||
|
f_email = f'{form.email.data}'.lower().strip()
|
||||||
|
f_isAdmin = form.isAdmin.data.strip().upper()
|
||||||
|
f_id = form.id.data.strip()
|
||||||
|
f_mnsj = None
|
||||||
|
q = None
|
||||||
|
t = None
|
||||||
|
subject = None
|
||||||
|
html_content = None
|
||||||
|
|
||||||
|
|
||||||
|
if f_id != '':
|
||||||
|
q = "UPDATE users SET nombre = %s, apellido = %s, genero = %s, email = %s, is_admin = %s WHERE id = %s;"
|
||||||
|
t = (f_nombre, f_apellido, f_genero, f_email, f_isAdmin, f_id)
|
||||||
|
f_mnsj = l_flash_msj('Éxito', f'Usuario actualizado: {f_nombre}', 'success')
|
||||||
|
subject = "Usuario actualizado"
|
||||||
|
html_content = """
|
||||||
|
<h1>¡Hola!</h1>
|
||||||
|
<p>Tu cuenta ha sido actualizada con éxito.</p>
|
||||||
|
<p><strong>Nombre:</strong> {}</p>
|
||||||
|
<p><strong>Apellido:</strong> {}</p>
|
||||||
|
<p><strong>Género:</strong> {}</p>
|
||||||
|
<p><strong>Email:</strong> {}</p>
|
||||||
|
""".format(f_nombre, f_apellido, f_genero, f_email)
|
||||||
|
|
||||||
|
else:
|
||||||
|
q_isDuplicated = "SELECT email FROM users WHERE email = %s;"
|
||||||
|
t_isDuplicated = (f_email,)
|
||||||
|
isDuplicated = dbUsers.get_data(q_isDuplicated, t_isDuplicated)
|
||||||
|
if isDuplicated is not None:
|
||||||
|
f_mnsj = l_flash_msj('Error', 'El correo electrónico ya existe.', 'error')
|
||||||
|
flash(f_mnsj)
|
||||||
|
return redirect(url_for('manage_profiles'))
|
||||||
|
|
||||||
|
|
||||||
|
random_id = getRandomId()
|
||||||
|
tmp_pswd = generar_contrasena()
|
||||||
|
hashed_pswd = hash_password(tmp_pswd)
|
||||||
|
q = "INSERT INTO users (id, nombre, apellido, genero, email, pswd, is_admin ) values (%s, %s, %s, %s, %s, %s, %s);"
|
||||||
|
t = (random_id, f_nombre, f_apellido, f_genero, f_email, hashed_pswd, f_isAdmin)
|
||||||
|
f_mnsj = l_flash_msj('Éxito', f'Usuario creado: {f_nombre}', 'success')
|
||||||
|
subject = "Nueva cuenta creada"
|
||||||
|
html_content = """
|
||||||
|
<h1>¡Bienvenido a Forma!</h1>
|
||||||
|
<p>Tu cuenta ha sido creada con éxito.</p>
|
||||||
|
<p><strong>Nombre:</strong> {}</p>
|
||||||
|
<p><strong>Apellido:</strong> {}</p>
|
||||||
|
<p><strong>Género:</strong> {}</p>
|
||||||
|
<p><strong>Email:</strong> {}</p>
|
||||||
|
<p><strong>Contraseña temporal:</strong> {}</p>
|
||||||
|
<p><strong>Una vez inicies sesión debes de cambiar la contraseña a una nueva que puedas recordar</strong></p>
|
||||||
|
""".format(f_nombre, f_apellido, f_genero, f_email, tmp_pswd)
|
||||||
|
|
||||||
|
# Crear el mensaje de correo con todo el contenido
|
||||||
|
msg = Message(
|
||||||
|
subject=subject,
|
||||||
|
sender=email_sender,
|
||||||
|
recipients=[f_email]
|
||||||
|
)
|
||||||
|
msg.html = html_content # Asignar el contenido HTML aquí
|
||||||
|
|
||||||
|
# Enviar en segundo plano
|
||||||
|
thr = Thread(target=send_async_email, args=(current_app._get_current_object(), msg))
|
||||||
|
thr.start()
|
||||||
|
|
||||||
|
dbUsers.update_data(q, t)
|
||||||
|
flash(f_mnsj)
|
||||||
|
return redirect(url_for('manage_profiles'))
|
||||||
|
|
||||||
|
return render_template(v['tmp_user']['manage_profiles'], form=form, data_all_users=data_all_users, active_page='manage_profiles', id_usr=id_usr)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/user/manage-profiles/delete-usr', methods=['GET', 'POST'])
|
||||||
|
@jwt_required()
|
||||||
|
@validate_user_exists
|
||||||
|
@admin_required
|
||||||
|
def delete_user():
|
||||||
|
data = request.get_json()
|
||||||
|
id_usr = data['id']
|
||||||
|
q_del_usr = "DELETE FROM users WHERE id = %s;"
|
||||||
|
dbUsers.update_data(q_del_usr, (id_usr,))
|
||||||
|
|
||||||
|
q_del_posts = "DELETE FROM posts WHERE id_usr = %s;"
|
||||||
|
dbUsers.update_data(q_del_posts, (id_usr,))
|
||||||
|
|
||||||
|
res = {'ok': True, 'message': f'El elemento se eliminó, pendiente manejar el error en el frontend', "id": id_usr}
|
||||||
|
return jsonify(res), 200
|
||||||
|
|
||||||
|
@app.route('/user/manage-profiles/get-user', methods=['POST']) # Cambiado a POST
|
||||||
|
@jwt_required()
|
||||||
|
@validate_user_exists
|
||||||
|
@admin_required
|
||||||
|
def get_user():
|
||||||
|
data = request.get_json()
|
||||||
|
id_usr = data['id']
|
||||||
|
q = "SELECT id, nombre, apellido, genero, email, is_admin FROM users WHERE id = %s;"
|
||||||
|
t = (id_usr,)
|
||||||
|
dbData = dbUsers.get_data(q, t)
|
||||||
|
return jsonify({"data": dbData})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/user/manage-profiles/update-user', methods=['POST'])
|
||||||
|
@jwt_required()
|
||||||
|
@validate_user_exists
|
||||||
|
@admin_required
|
||||||
|
def update_user():
|
||||||
|
data = request.get_json()
|
||||||
|
id_usr = data['id']
|
||||||
|
f_nombre = f'{data["nombre"]}'.title().strip()
|
||||||
|
f_apellido = f'{data["apellido"]}'.title().strip()
|
||||||
|
f_genero = data['genero']
|
||||||
|
f_email = f'{data["email"]}'.lower().strip()
|
||||||
|
f_isAdmin = data['isAdmin'].strip().lower()
|
||||||
|
|
||||||
|
q = "UPDATE users SET nombre = %s, apellido = %s, genero = %s, email = %s, is_admin = %s WHERE id = %s;"
|
||||||
|
|
||||||
|
t = (f_nombre, f_apellido, f_genero, f_email, f_isAdmin, id_usr)
|
||||||
|
|
||||||
|
dbUsers.update_data(q, t)
|
||||||
|
|
||||||
|
|
||||||
|
res = {'ok': True, 'message': 'El elemento se actualizó correctamente', "id": "id_usr"}
|
||||||
|
return jsonify(res), 200
|
||||||
|
|
||||||
|
|
||||||
# -------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
|
# MANEJO DE ERRORES DE JWT
|
||||||
|
|
||||||
|
@jwt.unauthorized_loader
|
||||||
|
def unauthorized_response(callback):
|
||||||
|
return jsonify({"error": "Token inválido o no proporcionado"}), 401
|
||||||
|
|
||||||
|
|
||||||
|
# Detecta si la petición viene del frontend (fetch)
|
||||||
|
def is_fetch_request():
|
||||||
|
return request.headers.get("X-Requested-With") == "XMLHttpRequest"
|
||||||
|
|
||||||
# Manejo de errores JWT
|
|
||||||
@jwt.expired_token_loader
|
@jwt.expired_token_loader
|
||||||
def handle_expired_token(jwt_header, jwt_payload):
|
def handle_expired_token(jwt_header, jwt_payload):
|
||||||
flash('Tu sesión ha expirado. Por favor inicia sesión nuevamente', 'warning')
|
# if is_fetch_request():
|
||||||
|
# return jsonify({"error": "El token ha expirado"}), 401
|
||||||
|
l_flash_msj('Sesión Expirada', 'Por favor inicia sesión nuevamente', 'warning')
|
||||||
|
|
||||||
|
# flash(l_flash_msj)
|
||||||
return redirect(url_for('login'))
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
@jwt.unauthorized_loader
|
@jwt.unauthorized_loader
|
||||||
def handle_unauthorized_error(reason):
|
def handle_unauthorized_error(reason):
|
||||||
|
if is_fetch_request():
|
||||||
|
return jsonify({"error": "Token inválido o no proporcionado"}), 401
|
||||||
flash(f'Debes iniciar sesión para acceder a esta página: {reason}', 'error')
|
flash(f'Debes iniciar sesión para acceder a esta página: {reason}', 'error')
|
||||||
return redirect(url_for('login'))
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
@jwt.invalid_token_loader
|
@jwt.invalid_token_loader
|
||||||
def handle_invalid_token_error(reason):
|
def handle_invalid_token_error(reason):
|
||||||
|
if is_fetch_request():
|
||||||
|
return jsonify({"error": "Sesión inválida"}), 401
|
||||||
flash(f'Sesión inválida: {reason}', 'error')
|
flash(f'Sesión inválida: {reason}', 'error')
|
||||||
return redirect(url_for('login'))
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
@app.route("/logout")
|
|
||||||
def logout():
|
|
||||||
response = make_response(redirect(url_for('login')))
|
|
||||||
response.delete_cookie('access_token_cookie')
|
|
||||||
flash('Sesión cerrada correctamente', 'success')
|
|
||||||
return response
|
|
||||||
|
|
||||||
# -------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
|
|
||||||
|
@app.route("/logout")
|
||||||
|
def logout():
|
||||||
|
f_mnsj = l_flash_msj('👋🏼Logout', 'Se ha cerrado tu sesión.', 'success')
|
||||||
|
flash(f_mnsj)
|
||||||
|
response = make_response(redirect(url_for('login')))
|
||||||
|
response.delete_cookie('access_token_cookie')
|
||||||
|
return response
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app.run(debug=True, host='0.0.0.0', port=8089)
|
app.run(debug=True, host='0.0.0.0', port=8089)
|
@ -1,4 +1,5 @@
|
|||||||
bcrypt==4.3.0
|
bcrypt==4.3.0
|
||||||
|
beautifulsoup4==4.13.3
|
||||||
blinker==1.9.0
|
blinker==1.9.0
|
||||||
click==8.1.8
|
click==8.1.8
|
||||||
dnspython==2.7.0
|
dnspython==2.7.0
|
||||||
@ -16,5 +17,7 @@ psycopg2-binary==2.9.10
|
|||||||
PyJWT==2.10.1
|
PyJWT==2.10.1
|
||||||
python-dotenv==1.1.0
|
python-dotenv==1.1.0
|
||||||
shortuuid==1.0.13
|
shortuuid==1.0.13
|
||||||
|
soupsieve==2.6
|
||||||
|
typing_extensions==4.13.1
|
||||||
Werkzeug==3.1.3
|
Werkzeug==3.1.3
|
||||||
WTForms==3.2.1
|
WTForms==3.2.1
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.r-i-ii {
|
.r-i-ii {
|
||||||
background-image: url('/static/img/home/reunion.avif');
|
background-image: url('/static/y_img/home/reunion.avif');
|
||||||
background-size: 100%;
|
background-size: 100%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
@ -1,210 +0,0 @@
|
|||||||
/**
|
|
||||||
* EXPANDING PANELS - Componente de paneles expandibles
|
|
||||||
*
|
|
||||||
* Estilos para un sistema de paneles con:
|
|
||||||
* - Efecto hover con animación
|
|
||||||
* - Texto expandible con scroll
|
|
||||||
* - Diseño responsive
|
|
||||||
*/
|
|
||||||
|
|
||||||
.expanding-panels {
|
|
||||||
display: grid;
|
|
||||||
gap: 1em;
|
|
||||||
|
|
||||||
/* ---------------------------- */
|
|
||||||
/* ESTILOS BASE DEL PANEL */
|
|
||||||
/* ---------------------------- */
|
|
||||||
.panel {
|
|
||||||
position: relative;
|
|
||||||
background-size: cover;
|
|
||||||
background-position: center;
|
|
||||||
border-radius: 10px;
|
|
||||||
overflow: hidden;
|
|
||||||
transition: all 0.5s ease;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---------------------------- */
|
|
||||||
/* CONTENIDO DEL PANEL */
|
|
||||||
/* ---------------------------- */
|
|
||||||
.panel-content {
|
|
||||||
text-align: center;
|
|
||||||
color: #000;
|
|
||||||
width: 90%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Título del panel - siempre visible */
|
|
||||||
.panel-title {
|
|
||||||
background-color: rgba(255, 255, 255, 0.85);
|
|
||||||
padding: 0.5em 1em;
|
|
||||||
border-radius: 10px;
|
|
||||||
transition: all 0.5s ease;
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Texto expandible - con scroll vertical cuando es necesario */
|
|
||||||
.panel-text {
|
|
||||||
max-height: 0;
|
|
||||||
opacity: 0;
|
|
||||||
overflow-y: hidden; /* Oculta scroll inicialmente */
|
|
||||||
transition: all 0.5s ease;
|
|
||||||
margin-top: 1em;
|
|
||||||
background-color: rgba(255, 255, 255, 0.85);
|
|
||||||
padding: 0 1em;
|
|
||||||
border-radius: 10px;
|
|
||||||
|
|
||||||
& p, ul {
|
|
||||||
text-align: justify;
|
|
||||||
text-justify: inter-word;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---------------------------- */
|
|
||||||
/* EFECTOS HOVER */
|
|
||||||
/* ---------------------------- */
|
|
||||||
.panel:hover {
|
|
||||||
transform: scale(1.05);
|
|
||||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.3);
|
|
||||||
z-index: 2;
|
|
||||||
|
|
||||||
.panel-text {
|
|
||||||
max-height: 50vh; /* Altura máxima antes de mostrar scroll */
|
|
||||||
opacity: 1;
|
|
||||||
padding: 1em;
|
|
||||||
overflow-y: auto; /* Muestra scroll solo cuando es necesario */
|
|
||||||
|
|
||||||
/* Estilos personalizados para la barra de scroll */
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
width: 6px;
|
|
||||||
}
|
|
||||||
&::-webkit-scrollbar-thumb {
|
|
||||||
background: rgba(0,0,0,0.2);
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---------------------------- */
|
|
||||||
/* IMÁGENES DE FONDO */
|
|
||||||
/* ---------------------------- */
|
|
||||||
.p1 { background-image: url('/static/img/about-us/team.avif'); }
|
|
||||||
.p2 { background-image: url('/static/img/about-us/mision.avif'); }
|
|
||||||
.p3 { background-image: url('/static/img/about-us/idea.avif'); }
|
|
||||||
.p4 { background-image: url('/static/img/about-us/mex.avif'); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================ */
|
|
||||||
/* MEDIA QUERIES - RESPONSIVE DESIGN */
|
|
||||||
/* ============================================ */
|
|
||||||
|
|
||||||
/* Smartphones (hasta 767px) */
|
|
||||||
@media (max-width: 767px) {
|
|
||||||
|
|
||||||
main{
|
|
||||||
width: 95vw;
|
|
||||||
margin: 1em auto;
|
|
||||||
min-height: 100vh;
|
|
||||||
margin-top: 1em;
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.expanding-panels {
|
|
||||||
grid-template-rows: repeat(4, 1fr);
|
|
||||||
row-gap: 2em;
|
|
||||||
|
|
||||||
.panel {
|
|
||||||
height: 45vh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tablets (768px - 1023px) */
|
|
||||||
@media (min-width: 768px) and (max-width: 1023px) {
|
|
||||||
|
|
||||||
main{
|
|
||||||
width: 90vw;
|
|
||||||
margin: auto;
|
|
||||||
min-height: 85vh;
|
|
||||||
margin-top: 1em;
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.expanding-panels {
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
|
|
||||||
.panel {
|
|
||||||
height: 45vh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Laptops (1024px - 1439px) */
|
|
||||||
@media (min-width: 1024px) and (max-width: 1439px) {
|
|
||||||
|
|
||||||
main{
|
|
||||||
width: 90vw;
|
|
||||||
margin: auto;
|
|
||||||
min-height: 85vh;
|
|
||||||
margin-top: 1em;
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.expanding-panels {
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
|
|
||||||
.panel {
|
|
||||||
height: 40vh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* PCs de escritorio (1440px - 1919px) */
|
|
||||||
@media (min-width: 1440px) and (max-width: 1919px) {
|
|
||||||
|
|
||||||
.expanding-panels {
|
|
||||||
grid-template-columns: repeat(4, 1fr);
|
|
||||||
|
|
||||||
.panel {
|
|
||||||
height: 75vh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
main{
|
|
||||||
width: 90vw;
|
|
||||||
margin: auto;
|
|
||||||
min-height: 85vh;
|
|
||||||
margin-top: 1em;
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pantallas Ultrawide (1920px en adelante) */
|
|
||||||
@media (min-width: 1920px) {
|
|
||||||
|
|
||||||
.expanding-panels {
|
|
||||||
grid-template-columns: repeat(4, 1fr);
|
|
||||||
|
|
||||||
.panel {
|
|
||||||
height: 75vh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
main{
|
|
||||||
width: 80vw;
|
|
||||||
margin: auto;
|
|
||||||
min-height: 85vh;
|
|
||||||
margin-top: 1em;
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
197
static/b_about-us/about-us.css
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
.expanding-panels {
|
||||||
|
display: grid;
|
||||||
|
gap: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------------- */
|
||||||
|
/* ESTILOS BASE DEL PANEL */
|
||||||
|
/* ---------------------------- */
|
||||||
|
.panel {
|
||||||
|
position: relative;
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all 0.5s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------------- */
|
||||||
|
/* CONTENIDO DEL PANEL */
|
||||||
|
/* ---------------------------- */
|
||||||
|
.panel-content {
|
||||||
|
text-align: center;
|
||||||
|
color: #000;
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-title {
|
||||||
|
background-color: rgba(255, 255, 255, 0.85);
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
border-radius: 10px;
|
||||||
|
transition: all 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-title h3 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-text {
|
||||||
|
max-height: 0;
|
||||||
|
opacity: 0;
|
||||||
|
overflow-y: hidden;
|
||||||
|
transition: all 0.5s ease;
|
||||||
|
margin-top: 1em;
|
||||||
|
background-color: rgba(255, 255, 255, 0.85);
|
||||||
|
padding: 0 1em;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-text p,
|
||||||
|
.panel-text ul {
|
||||||
|
text-align: justify;
|
||||||
|
text-justify: inter-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------------- */
|
||||||
|
/* EFECTOS HOVER */
|
||||||
|
/* ---------------------------- */
|
||||||
|
.panel:hover {
|
||||||
|
transform: scale(1.02);
|
||||||
|
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.3);
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel:hover .panel-title {
|
||||||
|
background-color: rgba(255, 255, 255, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel:hover .panel-text {
|
||||||
|
max-height: 50vh;
|
||||||
|
opacity: 1;
|
||||||
|
padding: 1em;
|
||||||
|
overflow-y: auto;
|
||||||
|
background-color: rgba(255, 255, 255, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Barra de scroll personalizada */
|
||||||
|
.panel-text::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-text::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(0, 0, 0, 0.2);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------------- */
|
||||||
|
/* IMÁGENES DE FONDO */
|
||||||
|
/* ---------------------------- */
|
||||||
|
.p1 { background-image: url('/static/y_img/about-us/team.avif'); }
|
||||||
|
.p2 { background-image: url('/static/y_img/about-us/mision.avif'); }
|
||||||
|
.p3 { background-image: url('/static/y_img/about-us/vision.avif'); }
|
||||||
|
.p4 { background-image: url('/static/y_img/about-us/valores.avif'); }
|
||||||
|
|
||||||
|
|
||||||
|
/* ============================================ */
|
||||||
|
/* MEDIA QUERIES - RESPONSIVE DESIGN */
|
||||||
|
/* ============================================ */
|
||||||
|
|
||||||
|
/* Smartphones (hasta 767px) */
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
|
||||||
|
main {
|
||||||
|
width: 95vw;
|
||||||
|
margin: 1em auto;
|
||||||
|
min-height: 100vh;
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expanding-panels {
|
||||||
|
grid-template-rows: repeat(4, 1fr);
|
||||||
|
row-gap: 2em;
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
height: 45vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tablets (768px - 1023px) */
|
||||||
|
@media (min-width: 768px) and (max-width: 1023px) {
|
||||||
|
|
||||||
|
main {
|
||||||
|
width: 90vw;
|
||||||
|
margin: auto;
|
||||||
|
min-height: 85vh;
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expanding-panels {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
height: 45vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Laptops (1024px - 1439px) */
|
||||||
|
@media (min-width: 1024px) and (max-width: 1439px) {
|
||||||
|
|
||||||
|
main {
|
||||||
|
width: 90vw;
|
||||||
|
margin: auto;
|
||||||
|
min-height: 85vh;
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expanding-panels {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
height: 40vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PCs de escritorio (1440px - 1919px) */
|
||||||
|
@media (min-width: 1440px) and (max-width: 1919px) {
|
||||||
|
|
||||||
|
.expanding-panels {
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
height: 75vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
width: 90vw;
|
||||||
|
margin: auto;
|
||||||
|
min-height: 85vh;
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pantallas Ultrawide (1920px en adelante) */
|
||||||
|
@media (min-width: 1920px) {
|
||||||
|
|
||||||
|
.expanding-panels {
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
height: 80vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,8 +1,7 @@
|
|||||||
main {
|
main {
|
||||||
background-image: url('/static/img/solutions/Imagen1.png');
|
background-image: url('/static/y_img/solutions/girl.png');
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center bottom;
|
background-position: center bottom;
|
||||||
background-color: #50164A;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -11,10 +10,8 @@ main {
|
|||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(3, 1fr);
|
grid-template-columns: repeat(3, 1fr);
|
||||||
grid-template-rows: repeat(3, 1fr);
|
grid-template-rows: repeat(3, 1fr);
|
||||||
gap: 2em;
|
/* gap: 2em; */
|
||||||
/* width: 75vw; */
|
|
||||||
/* Altura para que la grilla sea visible */
|
|
||||||
/* height: 80vh; */
|
|
||||||
& div {
|
& div {
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
@ -22,42 +19,43 @@ main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.parent div {
|
.parent div {
|
||||||
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||||
border: 2px solid transparent;
|
border: 2px solid transparent;
|
||||||
|
border-radius: 15px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
animation: vibrate 0.4s ease infinite;
|
animation: vibrate 0.4s ease infinite !important;
|
||||||
transform: scale(1.03);
|
transform: scale(1.05) !important;
|
||||||
box-shadow: 0 5px 15px rgba(255, 255, 255, 0.4);
|
box-shadow: 0 5px 15px rgba(255, 255, 255, 0.5) !important;
|
||||||
border-color: #00acc1;
|
border-color: #00acc1 !important;
|
||||||
filter: brightness(1.05);
|
filter: brightness(1.05) !important;
|
||||||
z-index: 10;
|
z-index: 10 !important;
|
||||||
border-radius: 15px;
|
border-radius: 15px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Imágenes específicas */
|
/* Imágenes específicas */
|
||||||
.i-i {
|
.i-i {
|
||||||
background-image: url('/static/img/solutions/Imagen2.png');
|
background-image: url('/static/y_img/solutions/reclutamiento_seleccion.png');
|
||||||
}
|
}
|
||||||
|
|
||||||
.i-ii {
|
.i-ii {
|
||||||
background-image: url('/static/img/solutions/Imagen3.png');
|
background-image: url('/static/y_img/solutions/liderazgo.png');
|
||||||
}
|
}
|
||||||
|
|
||||||
.i-iii {
|
.i-iii {
|
||||||
background-image: url('/static/img/solutions/Imagen4.png');
|
background-image: url('/static/y_img/solutions/capacitacion.png');
|
||||||
}
|
}
|
||||||
|
|
||||||
.ii-i {
|
.ii-i {
|
||||||
background-image: url('/static/img/solutions/Imagen5.png');
|
background-image: url('/static/y_img/solutions/cambio.png');
|
||||||
grid-row: 2;
|
grid-row: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ii-ii {
|
.ii-ii {
|
||||||
background-image: url('/static/img/solutions/Imagen6.png');
|
background-image: url('/static/y_img/solutions/objetivos.png');
|
||||||
grid-column: 3;
|
grid-column: 3;
|
||||||
grid-row: 2;
|
grid-row: 2;
|
||||||
}
|
}
|
||||||
@ -148,13 +146,6 @@ main {
|
|||||||
/* PCs de escritorio (1440px - 1919px) */
|
/* PCs de escritorio (1440px - 1919px) */
|
||||||
@media (min-width: 1440px) and (max-width: 1919px) {
|
@media (min-width: 1440px) and (max-width: 1919px) {
|
||||||
|
|
||||||
main {
|
|
||||||
width: 80vw;
|
|
||||||
min-height: 85vh;
|
|
||||||
margin: 10px auto;
|
|
||||||
background-size: auto 50%;
|
|
||||||
border-radius: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.parent {
|
.parent {
|
||||||
width: 70vw;
|
width: 70vw;
|
||||||
@ -162,24 +153,19 @@ main {
|
|||||||
height: 70vh;
|
height: 70vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Pantallas Ultrawide (1920px en adelante) */
|
/* Pantallas Ultrawide (1920px en adelante) */
|
||||||
@media (min-width: 1920px) {
|
@media (min-width: 1920px) {
|
||||||
|
|
||||||
main {
|
|
||||||
width: 80vw;
|
|
||||||
min-height: 85vh;
|
|
||||||
margin: 10px auto;
|
|
||||||
background-size: auto 50%;
|
|
||||||
border-radius: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.parent {
|
.parent {
|
||||||
width: 70vw;
|
width: 75vw;
|
||||||
/* Altura para que la grilla sea visible */
|
height: 40vw;
|
||||||
height: 70vh;
|
gap: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
background-size: 50% auto; /* 30% del ancho del contenedor */
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
5
static/d_methodology/methodology.css
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
.container{
|
||||||
|
& p {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
25
static/e_blog/a_all_posts.css
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
.card-img {
|
||||||
|
width: 100%;
|
||||||
|
height: 200px;
|
||||||
|
object-fit: cover;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
margin-bottom: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cat {
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-users {
|
||||||
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-footer {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
4
static/f_contact/contact.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import {simpleNotification} from '../z_comps/notify.js';
|
||||||
|
|
||||||
|
simpleNotification(data.title, data.body, data.typeAlert);
|
||||||
|
|
101
static/f_contact/form.css
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
.form-container {
|
||||||
|
/* max-width: 800px; */
|
||||||
|
min-width: 20vw;
|
||||||
|
margin: 2rem auto;
|
||||||
|
padding: 2rem;
|
||||||
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
|
||||||
|
/* font-family: 'Segoe UI', Roboto, sans-serif; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
gap: 1rem;
|
||||||
|
& label {
|
||||||
|
font-size: 1.1em;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-label {
|
||||||
|
flex: 0 0 180px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #2d3748;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control,
|
||||||
|
.form-select {
|
||||||
|
flex: 1;
|
||||||
|
padding: 0.75rem;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 1rem;
|
||||||
|
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control:focus,
|
||||||
|
.form-select:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #4299e1;
|
||||||
|
box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 200px;
|
||||||
|
margin: 1.5rem auto 0;
|
||||||
|
padding: 0.75rem;
|
||||||
|
background-color: #4299e1;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
background-color: #3182ce;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -------------------------- */
|
||||||
|
.form-footer {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
padding-top: 1rem;
|
||||||
|
border-top: 1px solid #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.forgot-password {
|
||||||
|
color: #4299e1;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.3rem 0.6rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.forgot-password:hover {
|
||||||
|
color: #3182ce;
|
||||||
|
text-decoration: underline;
|
||||||
|
background-color: rgba(66, 153, 225, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Opcional: Alineación con el botón si quieres que quede centrado debajo */
|
||||||
|
.form-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
18
static/f_contact/re_phone.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import {simpleNotification} from '../z_comps/notify.js';
|
||||||
|
|
||||||
|
// simpleNotification("test", "sample", "error");
|
||||||
|
|
||||||
|
const phoneInput = document.getElementById("num_tel");
|
||||||
|
|
||||||
|
phoneInput.addEventListener("change", () => {
|
||||||
|
let raw = phoneInput.value.replace(/\D/g, ''); // quitar todo lo que no sea número
|
||||||
|
|
||||||
|
if (raw.length === 10) {
|
||||||
|
// aplicar formato XX XXXX XXXX
|
||||||
|
const formatted = `${raw.slice(0, 2)} ${raw.slice(2, 6)} ${raw.slice(6, 10)}`;
|
||||||
|
phoneInput.value = formatted;
|
||||||
|
} else {
|
||||||
|
// no hace nada si no son 10 dígitos, pero podrías notificar si quieres
|
||||||
|
simpleNotification("Error", "El número de teléfono debe tener 10 dígitos.", "error");
|
||||||
|
}
|
||||||
|
});
|
102
static/h_tmp_user/add_pst.js
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import { simpleNotification } from '../z_comps/notify.js';
|
||||||
|
|
||||||
|
// https://summernote.org/getting-started/#without-bootstrap-lite
|
||||||
|
|
||||||
|
|
||||||
|
$('#summernote').summernote({
|
||||||
|
placeholder: 'Escribe aquí...',
|
||||||
|
tabsize: 2,
|
||||||
|
toolbar: [
|
||||||
|
['misc', ['undo', 'redo', 'clear']],
|
||||||
|
['heading', ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']], // tamaño de encabezado
|
||||||
|
['style', ['style','bold', 'italic', 'underline']],
|
||||||
|
['fontsize', ['fontsize']],
|
||||||
|
['color', ['color']],
|
||||||
|
['height', ['height']],
|
||||||
|
['font', ['strikethrough', 'superscript', 'subscript', 'clear']],
|
||||||
|
['para', ['ul', 'ol', 'paragraph']],
|
||||||
|
['object', ['link', 'table', 'picture', 'video', 'hr']],
|
||||||
|
['misc', [ 'help', 'codeview']], //'fullscreen'
|
||||||
|
['fontname', ['fontname']],
|
||||||
|
|
||||||
|
// ['alignment', ['justifyLeft', 'justifyCenter', 'justifyRight', 'justifyFull']],
|
||||||
|
// ['paragraph', ['indent', 'outdent']],
|
||||||
|
// ['cleaner', ['removeFormat']],
|
||||||
|
|
||||||
|
],
|
||||||
|
callbacks: {
|
||||||
|
onInit: function () {
|
||||||
|
const btn = document.getElementById('btn-submit');
|
||||||
|
const toolbar = document.querySelector('.note-toolbar');
|
||||||
|
const iTitle = document.querySelector('input[name="title"]');
|
||||||
|
|
||||||
|
if (btn && toolbar && iTitle) {
|
||||||
|
// Crear un contenedor para el título en una nueva línea
|
||||||
|
const titleWrapper = document.createElement('div');
|
||||||
|
titleWrapper.classList.add('note-title-wrapper', 'mb-2'); // margen inferior
|
||||||
|
|
||||||
|
// Clonar el input o moverlo (según tu preferencia)
|
||||||
|
titleWrapper.appendChild(iTitle);
|
||||||
|
|
||||||
|
// Insertar al inicio de la barra de herramientas
|
||||||
|
toolbar.prepend(titleWrapper);
|
||||||
|
|
||||||
|
// Crear un contenedor para el botón de enviar
|
||||||
|
const btnGroup = document.createElement('div');
|
||||||
|
btnGroup.classList.add('note-btn-group', 'ms-2'); // margen izquierdo
|
||||||
|
// agregar btn enviar cambios
|
||||||
|
btnGroup.appendChild(btn);
|
||||||
|
toolbar.appendChild(btnGroup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async function a_sendpost(title, body) {
|
||||||
|
try {
|
||||||
|
let response = await fetch('/user/save-post', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({"title": title, "body": body}),
|
||||||
|
credentials: 'include'
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Error en la respuesta del servidor <-');
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
simpleNotification('Éxito', `Publicación guardada: ${result.title_post}`, 'success');
|
||||||
|
// Opcional: Limpiar el editor después de guardar
|
||||||
|
$('#summernote').summernote('code', '');
|
||||||
|
document.querySelector("input[name='title']").value = '';
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error al guardar:', error);
|
||||||
|
simpleNotification('Error', 'No se pudo guardar la publicación', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function send_post(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
let title_post = document.querySelector("input[name='title']").value.trim();
|
||||||
|
let body_post = $('#summernote').summernote('code'); // Obtener el contenido del editor
|
||||||
|
let body_gt11 = body_post.length > 11 ? body_post : '';
|
||||||
|
|
||||||
|
if (!title_post || (!body_post || body_gt11 === '')) {
|
||||||
|
simpleNotification('Campos obligatorios', 'Todos los campos son obligatorios', 'warning');
|
||||||
|
} else {
|
||||||
|
a_sendpost(title_post, body_post);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("post-form").addEventListener("submit", send_post);
|
30
static/h_tmp_user/change_pswd.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// import { simpleNotification } from '../comps/notify.js';
|
||||||
|
import { simpleNotification } from '../z_comps/notify.js';
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
const form = document.querySelector('form');
|
||||||
|
const newPswdInput = document.querySelector('#new_pswd');
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
lengt10: (pwd) => pwd.length >= 10,
|
||||||
|
atl1num: (pwd) => /\d/.test(pwd),
|
||||||
|
atl1mayusc: (pwd) => /[A-Z]/.test(pwd),
|
||||||
|
atl1chrspe: (pwd) => /[!"#$%&\/()=?¡]/.test(pwd)
|
||||||
|
};
|
||||||
|
|
||||||
|
newPswdInput.addEventListener('input', function () {
|
||||||
|
const pwd = this.value;
|
||||||
|
for (let id in rules) {
|
||||||
|
document.querySelector(`#${id}`).textContent = rules[id](pwd) ? '✅' : '❌';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
form.addEventListener('submit', function (e) {
|
||||||
|
const pwd = newPswdInput.value;
|
||||||
|
const valid = Object.values(rules).every(fn => fn(pwd));
|
||||||
|
if (!valid) {
|
||||||
|
e.preventDefault();
|
||||||
|
simpleNotification("Error", "La contraseña no cumple con todos los requisitos indicados.", "error");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
164
static/h_tmp_user/edit-post.js
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
import { simpleNotification } from '../z_comps/notify.js';
|
||||||
|
|
||||||
|
|
||||||
|
// VACIAR LA INFORMACIÓN DE LA BASE DE DATOS EN LA UI
|
||||||
|
let title_post = data[0];
|
||||||
|
let body_post = data[1];
|
||||||
|
|
||||||
|
// https://summernote.org/getting-started/#without-bootstrap-lite
|
||||||
|
|
||||||
|
// configuración inicial de campo de editor de texto
|
||||||
|
// Inicializa Summernote con tus opciones
|
||||||
|
$('#summernote').summernote({
|
||||||
|
placeholder: 'Escribe aquí...',
|
||||||
|
tabsize: 2,
|
||||||
|
width: 1200,
|
||||||
|
height: 500,
|
||||||
|
toolbar: [
|
||||||
|
['misc', ['undo', 'redo', 'clear']],
|
||||||
|
['heading', ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']], // tamaño de encabezado
|
||||||
|
['style', ['style','bold', 'italic', 'underline']],
|
||||||
|
['fontsize', ['fontsize']],
|
||||||
|
['color', ['color']],
|
||||||
|
['height', ['height']],
|
||||||
|
['font', ['strikethrough', 'superscript', 'subscript', 'clear']],
|
||||||
|
['para', ['ul', 'ol', 'paragraph']],
|
||||||
|
['object', ['link', 'table', 'picture', 'video', 'hr']],
|
||||||
|
['misc', [ 'help', 'codeview']], //'fullscreen'
|
||||||
|
['fontname', ['fontname']],
|
||||||
|
|
||||||
|
],
|
||||||
|
callbacks: {
|
||||||
|
onInit: function() {
|
||||||
|
const btn_submit = document.getElementById('btn-submit');
|
||||||
|
const btn_cancel = document.getElementById('btn-cancel');
|
||||||
|
const toolbar = document.querySelector('.note-toolbar');
|
||||||
|
const iTitle = document.querySelector('input[name="title"]');
|
||||||
|
|
||||||
|
|
||||||
|
if (btn_submit && toolbar && iTitle) {
|
||||||
|
|
||||||
|
const titleWrapper = document.createElement('div');
|
||||||
|
titleWrapper.classList.add('note-title-wrapper', 'mb-2'); // margen inferior
|
||||||
|
|
||||||
|
// Clonar el input o moverlo (según tu preferencia)
|
||||||
|
titleWrapper.appendChild(iTitle);
|
||||||
|
|
||||||
|
// Insertar al inicio de la barra de herramientas
|
||||||
|
toolbar.prepend(titleWrapper);
|
||||||
|
|
||||||
|
// Crear un contenedor para el botón de enviar
|
||||||
|
const btnGroup = document.createElement('div');
|
||||||
|
btnGroup.classList.add('note-btn-group', 'ms-2');
|
||||||
|
|
||||||
|
// agregar btn enviar cambios
|
||||||
|
btnGroup.appendChild(btn_submit);
|
||||||
|
// agregar btn cancelar
|
||||||
|
btnGroup.appendChild(btn_cancel);
|
||||||
|
// agregar btnGroup al toolbar
|
||||||
|
toolbar.appendChild(btnGroup);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Luego, carga el contenido en el editor
|
||||||
|
$('#summernote').summernote('code', body_post);
|
||||||
|
|
||||||
|
// También puedes llenar el campo de título si quieres:
|
||||||
|
document.querySelector('[name="title"]').value = title_post;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ENVIAR LOS NUEVOS CAMBIOS A LA BASE DE DATOS
|
||||||
|
|
||||||
|
// async function a_sendpost(title, body, id) {
|
||||||
|
// try {
|
||||||
|
// let response = await fetch('/user/save-post', {
|
||||||
|
// method: 'POST',
|
||||||
|
// headers: {
|
||||||
|
// 'Content-Type': 'application/json'
|
||||||
|
// },
|
||||||
|
// body: JSON.stringify({ "id": id, "title": title, "body": body}),
|
||||||
|
// credentials: 'include'
|
||||||
|
// });
|
||||||
|
|
||||||
|
|
||||||
|
// if (!response.ok) {
|
||||||
|
// throw new Error('Error en la respuesta del servidor <-');
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const result = await response.json();
|
||||||
|
|
||||||
|
// simpleNotification('Éxito', `Publicación actualizada: ${result.title_post}`, 'success');
|
||||||
|
// // Opcional: Limpiar el editor después de guardar
|
||||||
|
// $('#summernote').summernote('code', '');
|
||||||
|
// document.querySelector("input[name='title']").value = '';
|
||||||
|
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error('Error al guardar:', error);
|
||||||
|
// simpleNotification('Error', 'No se pudo guardar la publicación', 'error');
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
async function a_sendpost(title, body, id) {
|
||||||
|
try {
|
||||||
|
let response = await fetch('/user/save-post', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ "id": id, "title": title, "body": body }),
|
||||||
|
credentials: 'include'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Error en la respuesta del servidor <-');
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
simpleNotification('Éxito', `Actualizando: ${result.title_post}...`, 'success'); // Añade tiempo
|
||||||
|
|
||||||
|
// Redirigir después de 2 segundos (puedes ajustar)
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href = result.redirect_url || '/user/my-posts';
|
||||||
|
}, 2000);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error al guardar:', error);
|
||||||
|
simpleNotification('Error', 'No se pudo guardar la publicación', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
document.getElementById('post-form').addEventListener('submit', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
let new_title_post = document.querySelector("input[name='title']").value.trim();
|
||||||
|
let new_body_post = $('#summernote').summernote('code'); // Obtener el contenido del editor
|
||||||
|
let body_gt11 = new_body_post.length > 11 ? new_body_post : '';
|
||||||
|
|
||||||
|
// Validación (descomenta cuando necesites)
|
||||||
|
if (!new_title_post || (!new_body_post || body_gt11 === '')) {
|
||||||
|
simpleNotification('Error', 'El título y el contenido son obligatorios', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// en caso que la información no cambie
|
||||||
|
if (new_title_post === title_post && new_body_post === body_post) {
|
||||||
|
simpleNotification('Sin Cambios', 'No se han realizado cambios en la publicación.', 'warning');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// en caso de que si hayan cambios
|
||||||
|
else{
|
||||||
|
// console.log(id_post);
|
||||||
|
a_sendpost(new_title_post, new_body_post, id_post);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
42
static/h_tmp_user/text_editor.css
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
.form-control{
|
||||||
|
width: 100% !important;
|
||||||
|
margin-bottom: 1em !important;
|
||||||
|
margin-top: 1em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-editor {
|
||||||
|
width: 100% !important;
|
||||||
|
min-height: 65vh !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-editable {
|
||||||
|
min-height: 65vh !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.note-toolbar{
|
||||||
|
background-color: #e9ecef;
|
||||||
|
padding: 1em !important;
|
||||||
|
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 999; /* para que esté por encima del contenido */
|
||||||
|
padding: 1em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.note-editing-area {
|
||||||
|
background-color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* aplica en la sección de edición del post */
|
||||||
|
#btn-cancel{
|
||||||
|
margin-left: 1em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* IMPORTANTE, UNA ANIMACIÓN DE AOS INTEFIERE CON SUMMERNOTE */
|
||||||
|
div.note-modal-backdrop {
|
||||||
|
z-index: 1 !important;
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 113 KiB |
Before Width: | Height: | Size: 773 KiB |
Before Width: | Height: | Size: 93 KiB |
Before Width: | Height: | Size: 1.5 MiB |
Before Width: | Height: | Size: 99 KiB |
Before Width: | Height: | Size: 191 KiB |
Before Width: | Height: | Size: 102 KiB |
Before Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 77 KiB |
@ -1,7 +1,14 @@
|
|||||||
|
:root {
|
||||||
|
--blue-formha: #5d9dd1;
|
||||||
|
--font-size-formha: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.navbar-custom {
|
.navbar-custom {
|
||||||
background-color: #50164A !important;
|
background-color: var(--blue-formha) !important;
|
||||||
font-size: 20px;
|
font-size: var(--font-size-formha);
|
||||||
|
padding-top: 15px;
|
||||||
|
padding-bottom: 15px;
|
||||||
/* min-height: 80px; */
|
/* min-height: 80px; */
|
||||||
|
|
||||||
& .navbar-brand, .nav-link {
|
& .navbar-brand, .nav-link {
|
||||||
@ -56,4 +63,59 @@
|
|||||||
margin-left: 1em;
|
margin-left: 1em;
|
||||||
margin-right: 1em;
|
margin-right: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* logo bordeado blanco */
|
||||||
|
& .logo{
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-image: url('../y_img/logos/formha_blanco_vertical.png');
|
||||||
|
width: 150px;
|
||||||
|
height: 100px;
|
||||||
|
background-size: cover; /* o cover según lo que busques */
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Estilos personalizados para el footer */
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
background-color: var(--blue-formha);
|
||||||
|
font-size: var(--font-size-formha);
|
||||||
|
color: white;
|
||||||
|
padding: 3rem 0;
|
||||||
|
|
||||||
|
& .img-cont{
|
||||||
|
padding: 0.25em;
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer h5 {
|
||||||
|
color: #f8f9fa;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer a {
|
||||||
|
color: #bdc3c7;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer a:hover {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.social-icons a {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 15px;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,13 +1,24 @@
|
|||||||
main {
|
body {
|
||||||
display: grid;
|
font-family: "Source Sans 3", sans-serif !important;
|
||||||
place-content: center;
|
|
||||||
}
|
}
|
||||||
/* main {
|
|
||||||
width: 80%;
|
|
||||||
|
/* Pantallas Ultrawide (1920px en adelante) */
|
||||||
|
@media (min-width: 1920px) {
|
||||||
|
main {
|
||||||
|
/* border: 6px solid red; */
|
||||||
|
/* max-width: 80vw !important; */
|
||||||
|
width: 80vw !important;
|
||||||
|
min-height: 85vh !important;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
text-align: center;
|
margin-top: 2em;
|
||||||
margin-top: 5em;
|
margin-bottom: 2em;
|
||||||
margin-bottom: 5em;
|
display: grid;
|
||||||
min-height: 85vh;
|
/* place-content: center; */
|
||||||
height: 85vh;
|
}
|
||||||
} */
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
BIN
static/y_img/about-us/mision.avif
Normal file
After Width: | Height: | Size: 352 KiB |
Before Width: | Height: | Size: 577 KiB After Width: | Height: | Size: 577 KiB |
BIN
static/y_img/about-us/valores.avif
Normal file
After Width: | Height: | Size: 418 KiB |
BIN
static/y_img/about-us/vision.avif
Normal file
After Width: | Height: | Size: 455 KiB |
BIN
static/y_img/favicon/favicon.ico
Normal file
After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 880 KiB After Width: | Height: | Size: 880 KiB |
Before Width: | Height: | Size: 463 KiB After Width: | Height: | Size: 463 KiB |
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 152 KiB |
Before Width: | Height: | Size: 446 KiB After Width: | Height: | Size: 446 KiB |
BIN
static/y_img/logos/circulo_logo.png
Normal file
After Width: | Height: | Size: 28 KiB |
27
static/y_img/logos/circulo_logo.svg
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg id="info" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 515.86 515.86">
|
||||||
|
<!-- Generator: Adobe Illustrator 29.4.0, SVG Export Plug-In . SVG Version: 2.1.0 Build 152) -->
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.st0 {
|
||||||
|
fill: #699dd3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.st1 {
|
||||||
|
fill: #003374;
|
||||||
|
}
|
||||||
|
|
||||||
|
.st2 {
|
||||||
|
fill: #861e5d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.st3 {
|
||||||
|
fill: #93b635;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<path class="st1" d="M257.93,0C115.48,0,0,115.48,0,257.93s115.48,257.93,257.93,257.93,257.93-115.48,257.93-257.93S400.38,0,257.93,0ZM257.93,374.4c-64.32,0-116.47-52.14-116.47-116.47s52.14-116.47,116.47-116.47,116.47,52.14,116.47,116.47-52.14,116.47-116.47,116.47Z"/>
|
||||||
|
<path class="st0" d="M257.93,17.99C125.41,17.99,17.99,125.41,17.99,257.93s107.43,239.94,239.94,239.94,239.94-107.43,239.94-239.94S390.45,17.99,257.93,17.99ZM257.93,374.4c-64.32,0-116.47-52.14-116.47-116.47s52.14-116.47,116.47-116.47,116.47,52.14,116.47,116.47-52.14,116.47-116.47,116.47Z"/>
|
||||||
|
<path class="st2" d="M152.7,307.88c-7.2-15.14-11.24-32.07-11.24-49.95s4.05-34.84,11.26-49.99c16.96-56.07,68.26-97.9,171.82-97.9,19.54,0,38.4,2.92,56.16,8.35,6.31,1.93,10.5-6.48,5.14-10.32-39.97-28.63-88.93-45.49-141.85-45.49C117.37,62.58,13.29,159.04,1.19,282.49c12.37,130.93,122.57,233.37,256.74,233.37,41.87,0,81.36-10.04,116.32-27.74-15.86,4.24-32.52,6.51-49.72,6.51-72.39,0-149.29-92.49-171.84-186.75Z"/>
|
||||||
|
<path class="st3" d="M497.87,300.26c0-18.96-3.56-37.08-9.99-53.78-.34-.88-.68-1.76-1.04-2.63-.5-1.26-1.05-2.5-1.58-3.74-1.68-3.88-3.45-7.72-5.43-11.43-.01.07-.02.13-.03.2-25.37-46.71-74.85-78.43-131.74-78.43-12.4,0-24.44,1.54-35.96,4.38,38.48,20.26,64.26,61.4,62.19,108.38-2.65,59.96-51.6,108.7-111.57,111.1-47.26,1.89-88.55-24.41-108.5-63.43-22.95-30.47-36.58-68.37-36.58-109.46,0-12.39,1.25-24.49,3.62-36.18,1.23-6.08-6.66-9.54-10.43-4.6-29.23,38.36-45.92,86.78-43.9,139.18,4.37,113.08,97.12,204.44,210.25,207.21,104.27,2.55,192.24-68.89,215.29-165.52.76-3.17,1.42-6.38,2.03-9.6,1.6-7.46,2.66-15.11,3.11-22.94.03-.33.07-.65.09-.98.1-2.31.18-4.62.18-6.96,0-.11-.01-.23-.01-.34,0-.14.01-.28.01-.42Z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
BIN
static/y_img/logos/formha_blanco_horizontal.png
Normal file
After Width: | Height: | Size: 27 KiB |
188
static/y_img/logos/formha_blanco_horizontal.svg
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
width="1183.704"
|
||||||
|
height="460.58533"
|
||||||
|
viewBox="0 0 1183.704 460.58532"
|
||||||
|
sodipodi:docname="logo_final.ai"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs1">
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath22">
|
||||||
|
<path
|
||||||
|
d="M 0,345.439 H 887.778 V 0 H 0 Z"
|
||||||
|
transform="translate(-213.37938,-127.86631)"
|
||||||
|
id="path22" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath24">
|
||||||
|
<path
|
||||||
|
d="M 0,345.439 H 887.778 V 0 H 0 Z"
|
||||||
|
transform="translate(-213.37938,-127.86631)"
|
||||||
|
id="path24" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath26">
|
||||||
|
<path
|
||||||
|
d="M 0,345.439 H 887.778 V 0 H 0 Z"
|
||||||
|
transform="translate(-172.8545,-153.4841)"
|
||||||
|
id="path26" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath28">
|
||||||
|
<path
|
||||||
|
d="M 0,345.439 H 887.778 V 0 H 0 Z"
|
||||||
|
transform="translate(-305.78463,-156.41581)"
|
||||||
|
id="path28" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath30">
|
||||||
|
<path
|
||||||
|
d="M 0,345.439 H 887.778 V 0 H 0 Z"
|
||||||
|
transform="translate(-350.95893,-135.9466)"
|
||||||
|
id="path30" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath32">
|
||||||
|
<path
|
||||||
|
d="M 0,345.439 H 887.778 V 0 H 0 Z"
|
||||||
|
transform="translate(-425.91109,-172.7705)"
|
||||||
|
id="path32" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath34">
|
||||||
|
<path
|
||||||
|
d="M 0,345.439 H 887.778 V 0 H 0 Z"
|
||||||
|
transform="translate(-508.43884,-178.3894)"
|
||||||
|
id="path34" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath36">
|
||||||
|
<path
|
||||||
|
d="M 0,345.439 H 887.778 V 0 H 0 Z"
|
||||||
|
transform="translate(-567.53771,-135.9466)"
|
||||||
|
id="path36" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath38">
|
||||||
|
<path
|
||||||
|
d="M 0,345.439 H 887.778 V 0 H 0 Z"
|
||||||
|
transform="translate(-653.37649,-135.9466)"
|
||||||
|
id="path38" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath40">
|
||||||
|
<path
|
||||||
|
d="M 0,345.439 H 887.778 V 0 H 0 Z"
|
||||||
|
transform="translate(-2.5000002e-6)"
|
||||||
|
id="path40" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#000000"
|
||||||
|
borderopacity="0.25"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1">
|
||||||
|
<inkscape:page
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
inkscape:label="2"
|
||||||
|
id="page20"
|
||||||
|
width="1183.704"
|
||||||
|
height="460.58533"
|
||||||
|
margin="97.848663 152.06134 97.849335"
|
||||||
|
bleed="0" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<g
|
||||||
|
id="layer-MC0"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
inkscape:label="fondo"
|
||||||
|
transform="translate(-761.34264)" />
|
||||||
|
<g
|
||||||
|
id="layer-MC1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
inkscape:label="info"
|
||||||
|
transform="translate(-761.34264)">
|
||||||
|
<path
|
||||||
|
id="path21"
|
||||||
|
d="m 0,0 c -24.771,0 -44.853,20.081 -44.853,44.853 0,24.772 20.082,44.853 44.853,44.853 24.772,0 44.853,-20.081 44.853,-44.853 C 44.853,20.081 24.772,0 0,0 m 0,144.186 c -54.86,0 -99.333,-44.473 -99.333,-99.333 0,-54.86 44.473,-99.333 99.333,-99.333 54.86,0 99.333,44.473 99.333,99.333 0,54.86 -44.473,99.333 -99.333,99.333"
|
||||||
|
style="fill:#29377c;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,1045.8485,290.09693)"
|
||||||
|
clip-path="url(#clipPath22)" />
|
||||||
|
<path
|
||||||
|
id="path23"
|
||||||
|
d="m 0,0 c -24.771,0 -44.853,20.081 -44.853,44.853 0,24.772 20.082,44.853 44.853,44.853 24.772,0 44.853,-20.081 44.853,-44.853 C 44.853,20.081 24.772,0 0,0 m 0,137.258 c -51.034,0 -92.405,-41.371 -92.405,-92.405 0,-51.034 41.371,-92.405 92.405,-92.405 51.034,0 92.405,41.371 92.405,92.405 0,51.034 -41.371,92.405 -92.405,92.405"
|
||||||
|
style="fill:#46a0de;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,1045.8485,290.09693)"
|
||||||
|
clip-path="url(#clipPath24)" />
|
||||||
|
<path
|
||||||
|
id="path25"
|
||||||
|
d="m 0,0 c -2.772,5.83 -4.328,12.35 -4.328,19.235 0,6.893 1.559,13.419 4.336,19.254 6.534,21.592 26.289,37.702 66.169,37.702 7.525,0 14.787,-1.125 21.628,-3.214 2.429,-0.742 4.045,2.495 1.98,3.974 -15.393,11.024 -34.249,17.518 -54.627,17.518 -48.766,0 -88.847,-37.15 -93.506,-84.692 4.765,-50.422 47.205,-89.875 98.873,-89.875 16.123,0 31.335,3.866 44.798,10.683 -6.107,-1.631 -12.524,-2.506 -19.146,-2.506 C 38.299,-71.921 8.685,-36.303 0,0"
|
||||||
|
style="fill:#9c135e;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,991.81533,255.93987)"
|
||||||
|
clip-path="url(#clipPath26)" />
|
||||||
|
<path
|
||||||
|
id="path27"
|
||||||
|
d="m 0,0 c 0,7.302 -1.37,14.281 -3.845,20.711 -0.131,0.34 -0.263,0.678 -0.4,1.015 -0.194,0.484 -0.403,0.962 -0.609,1.44 -0.646,1.496 -1.329,2.972 -2.091,4.402 -0.004,-0.026 -0.009,-0.051 -0.013,-0.077 -9.769,17.99 -28.826,30.204 -50.737,30.204 -4.776,0 -9.412,-0.591 -13.85,-1.685 14.82,-7.803 24.747,-23.648 23.948,-41.737 -1.019,-23.093 -19.872,-41.862 -42.969,-42.786 -18.2,-0.728 -34.101,9.401 -41.786,24.428 -8.841,11.736 -14.086,26.331 -14.086,42.156 0,4.772 0.481,9.431 1.392,13.933 0.475,2.344 -2.566,3.674 -4.015,1.773 -11.258,-14.772 -17.686,-33.419 -16.906,-53.6 1.684,-43.551 37.402,-78.734 80.972,-79.8 40.156,-0.982 74.036,26.531 82.912,63.745 0.292,1.222 0.547,2.456 0.783,3.698 0.617,2.871 1.024,5.82 1.196,8.833 0.01,0.126 0.027,0.25 0.036,0.376 0.04,0.889 0.068,1.78 0.068,2.679 0,0.044 -0.004,0.087 -0.004,0.131 C -0.004,-0.107 0,-0.054 0,0"
|
||||||
|
style="fill:#6bc62f;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,1169.0555,252.03093)"
|
||||||
|
clip-path="url(#clipPath28)" />
|
||||||
|
<path
|
||||||
|
id="path29"
|
||||||
|
d="M 0,0 V 73.548 H 50.42 V 61.106 H 14.85 V 43.696 H 45.553 V 31.254 H 14.85 V 0 Z"
|
||||||
|
style="fill:#646769;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,1229.2879,279.3232)"
|
||||||
|
clip-path="url(#clipPath30)" />
|
||||||
|
<path
|
||||||
|
id="path31"
|
||||||
|
d="m 0,0 c 0,-8.328 1.923,-14.641 5.769,-18.938 3.846,-4.299 8.73,-6.447 14.65,-6.447 5.92,0 10.777,2.132 14.573,6.396 3.796,4.265 5.695,10.661 5.695,19.19 0,8.428 -1.848,14.716 -5.543,18.863 -3.697,4.147 -8.605,6.221 -14.725,6.221 -6.121,0 -11.055,-2.099 -14.8,-6.296 C 1.873,14.791 0,8.462 0,0 m -15.301,-0.502 c 0,7.492 1.12,13.78 3.361,18.864 1.672,3.746 3.954,7.106 6.848,10.085 2.892,2.974 6.062,5.182 9.507,6.622 4.582,1.938 9.865,2.908 15.853,2.908 10.837,0 19.507,-3.361 26.013,-10.083 6.504,-6.723 9.758,-16.072 9.758,-28.045 0,-11.874 -3.229,-21.164 -9.683,-27.869 -6.455,-6.705 -15.084,-10.059 -25.887,-10.059 -10.937,0 -19.634,3.337 -26.088,10.01 -6.455,6.672 -9.682,15.861 -9.682,27.567"
|
||||||
|
style="fill:#646769;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,1329.2241,230.22467)"
|
||||||
|
clip-path="url(#clipPath32)" />
|
||||||
|
<path
|
||||||
|
id="path33"
|
||||||
|
d="m 0,0 h 10.987 c 7.124,0 11.571,0.301 13.345,0.903 1.773,0.602 3.161,1.639 4.164,3.111 1.003,1.471 1.505,3.311 1.505,5.518 0,2.475 -0.661,4.472 -1.981,5.995 -1.322,1.523 -3.186,2.485 -5.595,2.886 -1.204,0.165 -4.816,0.25 -10.836,0.25 H 0 Z m -14.85,-42.443 v 73.548 h 31.255 c 7.859,0 13.57,-0.661 17.133,-1.982 3.561,-1.322 6.413,-3.671 8.554,-7.049 2.139,-3.378 3.21,-7.242 3.21,-11.59 0,-5.516 -1.623,-10.075 -4.866,-13.669 -3.244,-3.597 -8.095,-5.863 -14.549,-6.8 3.211,-1.873 5.861,-3.928 7.952,-6.169 2.09,-2.241 4.908,-6.221 8.453,-11.941 l 8.981,-14.348 H 33.513 L 22.777,-26.44 c -3.813,5.721 -6.422,9.324 -7.827,10.813 -1.404,1.487 -2.893,2.508 -4.465,3.059 -1.573,0.553 -4.063,0.828 -7.475,0.828 H 0 v -30.703 z"
|
||||||
|
style="fill:#646769;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,1439.2611,222.7328)"
|
||||||
|
clip-path="url(#clipPath34)" />
|
||||||
|
<path
|
||||||
|
id="path35"
|
||||||
|
d="M 0,0 V 73.548 H 22.225 L 35.57,23.379 48.764,73.548 H 71.039 V 0 H 57.243 V 57.895 L 42.644,0 H 28.345 L 13.796,57.895 V 0 Z"
|
||||||
|
style="fill:#646769;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,1518.0596,279.3232)"
|
||||||
|
clip-path="url(#clipPath36)" />
|
||||||
|
<path
|
||||||
|
id="path37"
|
||||||
|
d="M 0,0 V 73.548 H 14.85 V 44.601 h 29.097 v 28.947 h 14.85 V 0 H 43.947 V 32.159 H 14.85 V 0 Z"
|
||||||
|
style="fill:#6bc62f;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,1632.5113,279.3232)"
|
||||||
|
clip-path="url(#clipPath38)" />
|
||||||
|
<path
|
||||||
|
id="path39"
|
||||||
|
d="m 753.463,210.747 h 12.141 v -12.09 h -12.141 z m 3.363,-49.214 c -1.806,-0.602 -4.667,-1.322 -8.58,-2.159 -3.913,-0.836 -6.471,-1.654 -7.675,-2.457 -1.842,-1.304 -2.76,-2.961 -2.76,-4.968 0,-1.973 0.735,-3.679 2.207,-5.115 1.472,-1.44 3.345,-2.158 5.619,-2.158 2.542,0 4.968,0.836 7.273,2.508 1.708,1.271 2.828,2.826 3.363,4.667 0.368,1.204 0.553,3.494 0.553,6.872 z m -24.935,49.214 h 12.092 v -12.09 h -12.092 z m 6.071,-37.776 -12.794,2.308 c 1.438,5.151 3.913,8.963 7.425,11.438 3.512,2.475 8.729,3.713 15.653,3.713 6.288,0 10.97,-0.746 14.047,-2.233 3.077,-1.487 5.243,-3.378 6.496,-5.668 1.256,-2.292 1.883,-6.498 1.883,-12.617 l -0.151,-16.455 c 0,-4.685 0.226,-8.138 0.677,-10.361 0.452,-2.225 1.296,-4.607 2.534,-7.149 h -13.947 c -0.368,0.936 -0.821,2.323 -1.354,4.163 -0.236,0.836 -0.401,1.389 -0.504,1.656 -2.408,-2.341 -4.983,-4.098 -7.724,-5.269 -2.744,-1.168 -5.67,-1.754 -8.78,-1.754 -5.485,0 -9.808,1.487 -12.969,4.466 -3.16,2.974 -4.74,6.738 -4.74,11.287 0,3.01 0.718,5.693 2.156,8.052 1.439,2.357 3.453,4.163 6.046,5.418 2.591,1.254 6.329,2.349 11.213,3.286 6.588,1.237 11.153,2.39 13.697,3.463 v 1.404 c 0,2.707 -0.671,4.639 -2.007,5.794 -1.34,1.153 -3.864,1.73 -7.577,1.73 -2.508,0 -4.464,-0.494 -5.868,-1.48 -1.405,-0.985 -2.544,-2.717 -3.412,-5.192"
|
||||||
|
style="fill:#46a0de;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,761.34267,460.58533)"
|
||||||
|
clip-path="url(#clipPath40)" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 10 KiB |
BIN
static/y_img/logos/formha_blanco_vertical.png
Normal file
After Width: | Height: | Size: 26 KiB |
185
static/y_img/logos/formha_blanco_vertical.svg
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
width="741.34265"
|
||||||
|
height="687.81335"
|
||||||
|
viewBox="0 0 741.34265 687.81335"
|
||||||
|
sodipodi:docname="logo_final.ai"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs1">
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath2">
|
||||||
|
<path
|
||||||
|
d="M 0,515.86 H 556.007 V 0 H 0 Z"
|
||||||
|
transform="translate(-278.00341,-257.5454)"
|
||||||
|
id="path2" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath4">
|
||||||
|
<path
|
||||||
|
d="M 0,515.86 H 556.007 V 0 H 0 Z"
|
||||||
|
transform="translate(-278.00341,-257.5454)"
|
||||||
|
id="path4" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath6">
|
||||||
|
<path
|
||||||
|
d="M 0,515.86 H 556.007 V 0 H 0 Z"
|
||||||
|
transform="translate(-232.5616,-286.27151)"
|
||||||
|
id="path6" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath8">
|
||||||
|
<path
|
||||||
|
d="M 0,515.86 H 556.007 V 0 H 0 Z"
|
||||||
|
transform="translate(-381.62011,-289.55881)"
|
||||||
|
id="path8" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath10">
|
||||||
|
<path
|
||||||
|
d="M 0,515.86 H 556.007 V 0 H 0 Z"
|
||||||
|
transform="translate(-116.36301,-97.5947)"
|
||||||
|
id="path10" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath12">
|
||||||
|
<path
|
||||||
|
d="M 0,515.86 H 556.007 V 0 H 0 Z"
|
||||||
|
transform="translate(-173.6766,-125.7527)"
|
||||||
|
id="path12" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath14">
|
||||||
|
<path
|
||||||
|
d="M 0,515.86 H 556.007 V 0 H 0 Z"
|
||||||
|
transform="translate(-236.7829,-130.0493)"
|
||||||
|
id="path14" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath16">
|
||||||
|
<path
|
||||||
|
d="M 0,515.86 H 556.007 V 0 H 0 Z"
|
||||||
|
transform="translate(-281.97391,-97.5947)"
|
||||||
|
id="path16" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath18">
|
||||||
|
<path
|
||||||
|
d="M 0,515.86 H 556.007 V 0 H 0 Z"
|
||||||
|
transform="translate(-347.61211,-97.5947)"
|
||||||
|
id="path18" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath20">
|
||||||
|
<path
|
||||||
|
d="M 0,515.86 H 556.007 V 0 H 0 Z"
|
||||||
|
id="path20" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#000000"
|
||||||
|
borderopacity="0.25"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1">
|
||||||
|
<inkscape:page
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
inkscape:label="1"
|
||||||
|
id="page1"
|
||||||
|
width="741.34265"
|
||||||
|
height="687.81335"
|
||||||
|
margin="128.84653 155.15067 128.84534"
|
||||||
|
bleed="0" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<g
|
||||||
|
id="layer-MC0"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
inkscape:label="fondo" />
|
||||||
|
<g
|
||||||
|
id="layer-MC1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
inkscape:label="info">
|
||||||
|
<path
|
||||||
|
id="path1"
|
||||||
|
d="m 0,0 c -27.777,0 -50.295,22.518 -50.295,50.295 0,27.777 22.518,50.295 50.295,50.295 27.777,0 50.295,-22.518 50.295,-50.295 C 50.295,22.518 27.777,0 0,0 m 0,161.68 c -61.516,0 -111.385,-49.869 -111.385,-111.385 0,-61.516 49.869,-111.385 111.385,-111.385 61.517,0 111.385,49.869 111.385,111.385 C 111.385,111.811 61.517,161.68 0,161.68"
|
||||||
|
style="fill:#29377c;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,370.6712,344.41947)"
|
||||||
|
clip-path="url(#clipPath2)" />
|
||||||
|
<path
|
||||||
|
id="path3"
|
||||||
|
d="m 0,0 c -27.777,0 -50.295,22.518 -50.295,50.295 0,27.777 22.518,50.295 50.295,50.295 27.777,0 50.295,-22.518 50.295,-50.295 C 50.295,22.518 27.777,0 0,0 m 0,153.912 c -57.226,0 -103.617,-46.391 -103.617,-103.617 0,-57.226 46.391,-103.617 103.617,-103.617 57.226,0 103.617,46.391 103.617,103.617 0,57.226 -46.391,103.617 -103.617,103.617"
|
||||||
|
style="fill:#46a0de;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,370.6712,344.41947)"
|
||||||
|
clip-path="url(#clipPath4)" />
|
||||||
|
<path
|
||||||
|
id="path5"
|
||||||
|
d="m 0,0 c -3.109,6.537 -4.853,13.849 -4.853,21.569 0,7.729 1.747,15.047 4.862,21.59 7.326,24.212 29.478,42.277 74.197,42.277 8.439,0 16.581,-1.262 24.252,-3.605 2.724,-0.831 4.536,2.798 2.221,4.456 -17.261,12.362 -38.405,19.644 -61.255,19.644 -54.683,0 -99.628,-41.658 -104.852,-94.968 5.343,-56.539 52.932,-100.779 110.87,-100.779 18.079,0 35.136,4.335 50.234,11.979 -6.849,-1.829 -14.044,-2.81 -21.47,-2.81 C 42.946,-80.647 9.739,-40.708 0,0"
|
||||||
|
style="fill:#9c135e;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,310.08213,306.118)"
|
||||||
|
clip-path="url(#clipPath6)" />
|
||||||
|
<path
|
||||||
|
id="path7"
|
||||||
|
d="m 0,0 c 0,8.188 -1.537,16.013 -4.312,23.224 -0.146,0.381 -0.295,0.76 -0.448,1.137 -0.218,0.544 -0.452,1.079 -0.683,1.616 -0.724,1.677 -1.49,3.333 -2.344,4.936 -0.005,-0.029 -0.011,-0.058 -0.015,-0.087 -10.954,20.173 -32.324,33.869 -56.893,33.869 -5.355,0 -10.554,-0.663 -15.531,-1.89 16.618,-8.749 27.75,-26.517 26.854,-46.801 -1.143,-25.894 -22.283,-46.94 -48.182,-47.976 -20.408,-0.816 -38.239,10.541 -46.857,27.392 -9.913,13.159 -15.795,29.525 -15.795,47.27 0,5.351 0.54,10.575 1.562,15.624 0.532,2.628 -2.878,4.12 -4.503,1.988 -12.623,-16.564 -19.832,-37.474 -18.957,-60.104 1.889,-48.834 41.94,-88.287 90.797,-89.481 45.027,-1.102 83.018,29.749 92.971,71.479 0.327,1.369 0.614,2.754 0.878,4.146 0.693,3.22 1.148,6.527 1.342,9.905 0.011,0.141 0.029,0.28 0.04,0.421 C -0.032,-2.335 0,-1.335 0,-0.327 0,-0.278 -0.005,-0.23 -0.005,-0.181 -0.005,-0.12 0,-0.061 0,0"
|
||||||
|
style="fill:#6bc62f;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,508.8268,301.73493)"
|
||||||
|
clip-path="url(#clipPath8)" />
|
||||||
|
<path
|
||||||
|
id="path9"
|
||||||
|
d="M 0,0 V 56.239 H 38.554 V 46.726 H 11.355 V 33.413 H 34.833 V 23.899 H 11.355 V 0 Z"
|
||||||
|
style="fill:#646769;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,155.15067,557.68707)"
|
||||||
|
clip-path="url(#clipPath10)" />
|
||||||
|
<path
|
||||||
|
id="path11"
|
||||||
|
d="m 0,0 c 0,-6.368 1.471,-11.196 4.412,-14.481 2.94,-3.288 6.675,-4.93 11.202,-4.93 4.526,0 8.241,1.63 11.143,4.89 2.903,3.262 4.355,8.153 4.355,14.674 0,6.445 -1.413,11.253 -4.239,14.425 -2.826,3.171 -6.579,4.757 -11.259,4.757 -4.681,0 -8.454,-1.606 -11.317,-4.814 C 1.432,11.31 0,6.47 0,0 m -11.701,-0.384 c 0,5.729 0.857,10.537 2.571,14.425 1.278,2.864 3.024,5.434 5.237,7.711 2.211,2.275 4.635,3.963 7.269,5.064 3.504,1.482 7.544,2.224 12.122,2.224 8.287,0 14.917,-2.57 19.892,-7.71 4.973,-5.141 7.461,-12.29 7.461,-21.446 0,-9.079 -2.469,-16.183 -7.404,-21.31 -4.936,-5.127 -11.534,-7.692 -19.795,-7.692 -8.363,0 -15.013,2.552 -19.949,7.655 -4.936,5.101 -7.404,12.128 -7.404,21.079"
|
||||||
|
style="fill:#646769;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,231.5688,520.14307)"
|
||||||
|
clip-path="url(#clipPath12)" />
|
||||||
|
<path
|
||||||
|
id="path13"
|
||||||
|
d="m 0,0 h 8.401 c 5.448,0 8.849,0.23 10.205,0.691 1.355,0.46 2.417,1.253 3.184,2.378 0.767,1.125 1.151,2.532 1.151,4.22 0,1.892 -0.506,3.419 -1.515,4.584 -1.011,1.164 -2.437,1.9 -4.278,2.207 -0.921,0.126 -3.683,0.191 -8.286,0.191 H 0 Z m -11.355,-32.455 v 56.24 h 23.9 c 6.009,0 10.376,-0.506 13.101,-1.515 2.723,-1.011 4.903,-2.807 6.54,-5.39 1.636,-2.584 2.455,-5.538 2.455,-8.863 0,-4.218 -1.241,-7.704 -3.721,-10.453 -2.481,-2.75 -6.19,-4.483 -11.125,-5.199 2.455,-1.432 4.482,-3.004 6.081,-4.718 1.598,-1.713 3.753,-4.757 6.464,-9.13 l 6.866,-10.972 h -13.58 l -8.209,12.237 c -2.916,4.375 -4.911,7.13 -5.985,8.268 -1.074,1.138 -2.212,1.919 -3.414,2.34 -1.203,0.423 -3.108,0.633 -5.716,0.633 H 0 v -23.478 z"
|
||||||
|
style="fill:#646769;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,315.71053,514.41427)"
|
||||||
|
clip-path="url(#clipPath14)" />
|
||||||
|
<path
|
||||||
|
id="path15"
|
||||||
|
d="M 0,0 V 56.239 H 16.995 L 27.199,17.877 37.288,56.239 H 54.321 V 0 H 43.772 V 44.27 L 32.608,0 H 21.675 L 10.55,44.27 V 0 Z"
|
||||||
|
style="fill:#646769;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,375.9652,557.68707)"
|
||||||
|
clip-path="url(#clipPath16)" />
|
||||||
|
<path
|
||||||
|
id="path17"
|
||||||
|
d="M 0,0 V 56.239 H 11.355 V 34.105 h 22.25 V 56.239 H 44.96 V 0 H 33.605 V 24.591 H 11.355 V 0 Z"
|
||||||
|
style="fill:#6bc62f;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,463.4828,557.68707)"
|
||||||
|
clip-path="url(#clipPath18)" />
|
||||||
|
<path
|
||||||
|
id="path19"
|
||||||
|
d="m 424.145,154.792 h 9.284 v -9.245 h -9.284 z m 2.571,-37.632 c -1.381,-0.461 -3.568,-1.011 -6.56,-1.651 -2.993,-0.639 -4.948,-1.265 -5.869,-1.879 -1.408,-0.997 -2.111,-2.264 -2.111,-3.798 0,-1.509 0.563,-2.814 1.688,-3.912 1.125,-1.101 2.558,-1.65 4.297,-1.65 1.943,0 3.798,0.639 5.562,1.918 1.305,0.972 2.161,2.16 2.571,3.568 0.281,0.921 0.422,2.672 0.422,5.255 z m -19.067,37.632 h 9.246 v -9.245 h -9.246 z m 4.643,-28.886 -9.783,1.765 c 1.1,3.938 2.992,6.854 5.677,8.747 2.686,1.892 6.676,2.838 11.97,2.838 4.808,0 8.388,-0.57 10.741,-1.707 2.353,-1.138 4.009,-2.583 4.968,-4.334 0.959,-1.753 1.439,-4.969 1.439,-9.648 l -0.115,-12.583 c 0,-3.582 0.173,-6.222 0.517,-7.922 0.346,-1.702 0.991,-3.524 1.938,-5.467 h -10.665 c -0.281,0.716 -0.627,1.776 -1.035,3.183 -0.181,0.639 -0.307,1.062 -0.385,1.267 -1.842,-1.791 -3.811,-3.134 -5.907,-4.029 -2.098,-0.894 -4.335,-1.342 -6.714,-1.342 -4.194,0 -7.499,1.137 -9.917,3.415 -2.416,2.275 -3.624,5.153 -3.624,8.631 0,2.302 0.549,4.354 1.649,6.157 1.1,1.803 2.64,3.184 4.623,4.144 1.981,0.958 4.84,1.796 8.574,2.512 5.038,0.946 8.528,1.828 10.473,2.648 v 1.074 c 0,2.07 -0.513,3.547 -1.534,4.43 -1.025,0.882 -2.955,1.323 -5.794,1.323 -1.918,0 -3.413,-0.378 -4.487,-1.132 -1.074,-0.753 -1.946,-2.077 -2.609,-3.97"
|
||||||
|
style="fill:#46a0de;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,0,687.81333)"
|
||||||
|
clip-path="url(#clipPath20)" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 10 KiB |
BIN
static/y_img/logos/formha_negro_horizontal.png
Normal file
After Width: | Height: | Size: 26 KiB |
194
static/y_img/logos/formha_negro_horizontal.svg
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
width="1183.704"
|
||||||
|
height="460.58533"
|
||||||
|
viewBox="0 0 1183.704 460.58532"
|
||||||
|
sodipodi:docname="logo_final.ai"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs1">
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath64">
|
||||||
|
<path
|
||||||
|
d="M 0,345.439 H 887.778 V 0 H 0 Z"
|
||||||
|
transform="translate(-213.37941,-127.86631)"
|
||||||
|
id="path64" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath66">
|
||||||
|
<path
|
||||||
|
d="M 0,345.439 H 887.778 V 0 H 0 Z"
|
||||||
|
transform="translate(-213.37941,-127.86631)"
|
||||||
|
id="path66" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath68">
|
||||||
|
<path
|
||||||
|
d="M 0,345.439 H 887.778 V 0 H 0 Z"
|
||||||
|
transform="translate(-172.8545,-153.4841)"
|
||||||
|
id="path68" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath70">
|
||||||
|
<path
|
||||||
|
d="M 0,345.439 H 887.778 V 0 H 0 Z"
|
||||||
|
transform="translate(-305.78458,-156.41581)"
|
||||||
|
id="path70" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath72">
|
||||||
|
<path
|
||||||
|
d="M 0,345.439 H 887.778 V 0 H 0 Z"
|
||||||
|
transform="translate(-350.95888,-135.9466)"
|
||||||
|
id="path72" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath74">
|
||||||
|
<path
|
||||||
|
d="M 0,345.439 H 887.778 V 0 H 0 Z"
|
||||||
|
transform="translate(-425.91111,-172.7705)"
|
||||||
|
id="path74" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath76">
|
||||||
|
<path
|
||||||
|
d="M 0,345.439 H 887.778 V 0 H 0 Z"
|
||||||
|
transform="translate(-508.43879,-178.3894)"
|
||||||
|
id="path76" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath78">
|
||||||
|
<path
|
||||||
|
d="M 0,345.439 H 887.778 V 0 H 0 Z"
|
||||||
|
transform="translate(-567.53774,-135.9466)"
|
||||||
|
id="path78" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath80">
|
||||||
|
<path
|
||||||
|
d="M 0,345.439 H 887.778 V 0 H 0 Z"
|
||||||
|
transform="translate(-653.37652,-135.9466)"
|
||||||
|
id="path80" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath82">
|
||||||
|
<path
|
||||||
|
d="M 0,345.439 H 887.778 V 0 H 0 Z"
|
||||||
|
transform="translate(2.5000001e-5)"
|
||||||
|
id="path82" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#000000"
|
||||||
|
borderopacity="0.25"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1">
|
||||||
|
<inkscape:page
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
inkscape:label="4"
|
||||||
|
id="page61"
|
||||||
|
width="1183.704"
|
||||||
|
height="460.58533"
|
||||||
|
margin="0"
|
||||||
|
bleed="0" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<g
|
||||||
|
id="layer-MC0"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
inkscape:label="fondo"
|
||||||
|
transform="translate(-2726.3893)">
|
||||||
|
<path
|
||||||
|
id="path62"
|
||||||
|
d="M 0,0 H 887.778 V 345.439 H 0 Z"
|
||||||
|
style="fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,2726.3893,460.58533)" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="layer-MC1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
inkscape:label="info"
|
||||||
|
transform="translate(-2726.3893)">
|
||||||
|
<path
|
||||||
|
id="path63"
|
||||||
|
d="m 0,0 c -24.771,0 -44.853,20.081 -44.853,44.853 0,24.772 20.082,44.853 44.853,44.853 24.772,0 44.853,-20.081 44.853,-44.853 C 44.853,20.081 24.772,0 0,0 m 0,144.186 c -54.86,0 -99.333,-44.473 -99.333,-99.333 0,-54.86 44.473,-99.333 99.333,-99.333 54.86,0 99.333,44.473 99.333,99.333 0,54.86 -44.473,99.333 -99.333,99.333"
|
||||||
|
style="fill:#29377c;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,3010.8952,290.09693)"
|
||||||
|
clip-path="url(#clipPath64)" />
|
||||||
|
<path
|
||||||
|
id="path65"
|
||||||
|
d="m 0,0 c -24.771,0 -44.853,20.081 -44.853,44.853 0,24.772 20.082,44.853 44.853,44.853 24.772,0 44.853,-20.081 44.853,-44.853 C 44.853,20.081 24.772,0 0,0 m 0,137.258 c -51.034,0 -92.405,-41.371 -92.405,-92.405 0,-51.034 41.371,-92.405 92.405,-92.405 51.034,0 92.405,41.371 92.405,92.405 0,51.034 -41.371,92.405 -92.405,92.405"
|
||||||
|
style="fill:#46a0de;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,3010.8952,290.09693)"
|
||||||
|
clip-path="url(#clipPath66)" />
|
||||||
|
<path
|
||||||
|
id="path67"
|
||||||
|
d="m 0,0 c -2.772,5.83 -4.328,12.35 -4.328,19.235 0,6.893 1.559,13.419 4.336,19.254 6.534,21.592 26.289,37.702 66.169,37.702 7.525,0 14.787,-1.125 21.628,-3.214 2.429,-0.742 4.045,2.495 1.98,3.974 -15.393,11.024 -34.249,17.518 -54.627,17.518 -48.766,0 -88.847,-37.15 -93.506,-84.692 4.765,-50.422 47.205,-89.875 98.873,-89.875 16.123,0 31.335,3.866 44.798,10.683 -6.107,-1.631 -12.524,-2.506 -19.146,-2.506 C 38.299,-71.921 8.685,-36.303 0,0"
|
||||||
|
style="fill:#9c135e;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,2956.862,255.93987)"
|
||||||
|
clip-path="url(#clipPath68)" />
|
||||||
|
<path
|
||||||
|
id="path69"
|
||||||
|
d="m 0,0 c 0,7.302 -1.37,14.281 -3.845,20.711 -0.131,0.34 -0.263,0.678 -0.4,1.015 -0.194,0.484 -0.403,0.962 -0.609,1.44 -0.646,1.496 -1.329,2.972 -2.091,4.402 -0.004,-0.026 -0.009,-0.051 -0.013,-0.077 -9.769,17.99 -28.826,30.204 -50.737,30.204 -4.776,0 -9.412,-0.591 -13.85,-1.685 14.82,-7.803 24.747,-23.648 23.948,-41.737 -1.019,-23.093 -19.872,-41.862 -42.969,-42.786 -18.2,-0.728 -34.101,9.401 -41.786,24.428 -8.841,11.736 -14.086,26.331 -14.086,42.156 0,4.772 0.481,9.431 1.392,13.933 0.475,2.344 -2.566,3.674 -4.015,1.773 -11.258,-14.772 -17.686,-33.419 -16.906,-53.6 1.684,-43.551 37.402,-78.734 80.972,-79.8 40.156,-0.982 74.036,26.531 82.912,63.745 0.292,1.222 0.547,2.456 0.783,3.698 0.617,2.871 1.024,5.82 1.196,8.833 0.01,0.126 0.027,0.25 0.036,0.376 0.04,0.889 0.068,1.78 0.068,2.679 0,0.044 -0.004,0.087 -0.004,0.131 C -0.004,-0.107 0,-0.054 0,0"
|
||||||
|
style="fill:#6bc62f;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,3134.1021,252.03093)"
|
||||||
|
clip-path="url(#clipPath70)" />
|
||||||
|
<path
|
||||||
|
id="path71"
|
||||||
|
d="M 0,0 V 73.548 H 50.42 V 61.106 H 14.85 V 43.696 H 45.553 V 31.254 H 14.85 V 0 Z"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,3194.3345,279.3232)"
|
||||||
|
clip-path="url(#clipPath72)" />
|
||||||
|
<path
|
||||||
|
id="path73"
|
||||||
|
d="m 0,0 c 0,-8.328 1.923,-14.641 5.769,-18.938 3.846,-4.299 8.73,-6.447 14.65,-6.447 5.92,0 10.777,2.132 14.573,6.396 3.796,4.265 5.695,10.661 5.695,19.19 0,8.428 -1.848,14.716 -5.543,18.863 -3.697,4.147 -8.605,6.221 -14.725,6.221 -6.121,0 -11.055,-2.099 -14.8,-6.296 C 1.873,14.791 0,8.462 0,0 m -15.301,-0.502 c 0,7.492 1.12,13.78 3.361,18.864 1.672,3.746 3.954,7.106 6.848,10.085 2.892,2.974 6.062,5.182 9.507,6.622 4.582,1.938 9.865,2.908 15.853,2.908 10.837,0 19.507,-3.361 26.013,-10.083 6.504,-6.723 9.758,-16.072 9.758,-28.045 0,-11.874 -3.229,-21.164 -9.683,-27.869 -6.455,-6.705 -15.084,-10.059 -25.887,-10.059 -10.937,0 -19.634,3.337 -26.088,10.01 -6.455,6.672 -9.682,15.861 -9.682,27.567"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,3294.2708,230.22467)"
|
||||||
|
clip-path="url(#clipPath74)" />
|
||||||
|
<path
|
||||||
|
id="path75"
|
||||||
|
d="m 0,0 h 10.987 c 7.124,0 11.571,0.301 13.345,0.903 1.773,0.602 3.161,1.639 4.164,3.111 1.003,1.471 1.505,3.311 1.505,5.518 0,2.475 -0.661,4.472 -1.981,5.995 -1.322,1.523 -3.186,2.485 -5.595,2.886 -1.204,0.165 -4.816,0.25 -10.836,0.25 H 0 Z m -14.85,-42.443 v 73.548 h 31.255 c 7.859,0 13.57,-0.661 17.133,-1.982 3.561,-1.322 6.413,-3.671 8.554,-7.049 2.139,-3.378 3.21,-7.242 3.21,-11.59 0,-5.516 -1.623,-10.075 -4.866,-13.669 -3.244,-3.597 -8.095,-5.863 -14.549,-6.8 3.211,-1.873 5.861,-3.928 7.952,-6.169 2.09,-2.241 4.908,-6.221 8.453,-11.941 l 8.981,-14.348 H 33.513 L 22.777,-26.44 c -3.813,5.721 -6.422,9.324 -7.827,10.813 -1.404,1.487 -2.893,2.508 -4.465,3.059 -1.573,0.553 -4.063,0.828 -7.475,0.828 H 0 v -30.703 z"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,3404.3077,222.7328)"
|
||||||
|
clip-path="url(#clipPath76)" />
|
||||||
|
<path
|
||||||
|
id="path77"
|
||||||
|
d="M 0,0 V 73.548 H 22.225 L 35.57,23.379 48.764,73.548 H 71.039 V 0 H 57.243 V 57.895 L 42.644,0 H 28.345 L 13.796,57.895 V 0 Z"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,3483.1063,279.3232)"
|
||||||
|
clip-path="url(#clipPath78)" />
|
||||||
|
<path
|
||||||
|
id="path79"
|
||||||
|
d="M 0,0 V 73.548 H 14.85 V 44.601 h 29.097 v 28.947 h 14.85 V 0 H 43.947 V 32.159 H 14.85 V 0 Z"
|
||||||
|
style="fill:#6bc62f;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,3597.558,279.3232)"
|
||||||
|
clip-path="url(#clipPath80)" />
|
||||||
|
<path
|
||||||
|
id="path81"
|
||||||
|
d="m 753.463,210.747 h 12.141 v -12.09 h -12.141 z m 3.363,-49.214 c -1.806,-0.602 -4.667,-1.322 -8.58,-2.159 -3.913,-0.836 -6.471,-1.654 -7.675,-2.457 -1.842,-1.304 -2.76,-2.961 -2.76,-4.968 0,-1.973 0.735,-3.679 2.207,-5.115 1.472,-1.44 3.345,-2.158 5.619,-2.158 2.542,0 4.968,0.836 7.273,2.508 1.708,1.271 2.828,2.826 3.363,4.667 0.368,1.204 0.553,3.494 0.553,6.872 z m -24.935,49.214 h 12.092 v -12.09 h -12.092 z m 6.071,-37.776 -12.794,2.308 c 1.438,5.151 3.913,8.963 7.425,11.438 3.512,2.475 8.729,3.713 15.653,3.713 6.288,0 10.97,-0.746 14.047,-2.233 3.077,-1.487 5.243,-3.378 6.496,-5.668 1.256,-2.292 1.883,-6.498 1.883,-12.617 l -0.151,-16.455 c 0,-4.685 0.226,-8.138 0.677,-10.361 0.452,-2.225 1.296,-4.607 2.534,-7.149 h -13.947 c -0.368,0.936 -0.821,2.323 -1.354,4.163 -0.236,0.836 -0.401,1.389 -0.504,1.656 -2.408,-2.341 -4.983,-4.098 -7.724,-5.269 -2.744,-1.168 -5.67,-1.754 -8.78,-1.754 -5.485,0 -9.808,1.487 -12.969,4.466 -3.16,2.974 -4.74,6.738 -4.74,11.287 0,3.01 0.718,5.693 2.156,8.052 1.439,2.357 3.453,4.163 6.046,5.418 2.591,1.254 6.329,2.349 11.213,3.286 6.588,1.237 11.153,2.39 13.697,3.463 v 1.404 c 0,2.707 -0.671,4.639 -2.007,5.794 -1.34,1.153 -3.864,1.73 -7.577,1.73 -2.508,0 -4.464,-0.494 -5.868,-1.48 -1.405,-0.985 -2.544,-2.717 -3.412,-5.192"
|
||||||
|
style="fill:#46a0de;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,2726.3893,460.58533)"
|
||||||
|
clip-path="url(#clipPath82)" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 11 KiB |
BIN
static/y_img/logos/formha_negro_vertical.png
Normal file
After Width: | Height: | Size: 26 KiB |
194
static/y_img/logos/formha_negro_vertical.svg
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
width="741.34265"
|
||||||
|
height="687.81335"
|
||||||
|
viewBox="0 0 741.34266 687.81334"
|
||||||
|
sodipodi:docname="logo_final.ai"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs1">
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath43">
|
||||||
|
<path
|
||||||
|
d="M 0,515.86 H 556.007 V 0 H 0 Z"
|
||||||
|
transform="translate(-278.00343,-257.5454)"
|
||||||
|
id="path43" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath45">
|
||||||
|
<path
|
||||||
|
d="M 0,515.86 H 556.007 V 0 H 0 Z"
|
||||||
|
transform="translate(-278.00343,-257.5454)"
|
||||||
|
id="path45" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath47">
|
||||||
|
<path
|
||||||
|
d="M 0,515.86 H 556.007 V 0 H 0 Z"
|
||||||
|
transform="translate(-232.56161,-286.27151)"
|
||||||
|
id="path47" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath49">
|
||||||
|
<path
|
||||||
|
d="M 0,515.86 H 556.007 V 0 H 0 Z"
|
||||||
|
transform="translate(-381.62013,-289.55881)"
|
||||||
|
id="path49" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath51">
|
||||||
|
<path
|
||||||
|
d="M 0,515.86 H 556.007 V 0 H 0 Z"
|
||||||
|
transform="translate(-116.36298,-97.5947)"
|
||||||
|
id="path51" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath53">
|
||||||
|
<path
|
||||||
|
d="M 0,515.86 H 556.007 V 0 H 0 Z"
|
||||||
|
transform="translate(-173.67663,-125.7527)"
|
||||||
|
id="path53" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath55">
|
||||||
|
<path
|
||||||
|
d="M 0,515.86 H 556.007 V 0 H 0 Z"
|
||||||
|
transform="translate(-236.78291,-130.0493)"
|
||||||
|
id="path55" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath57">
|
||||||
|
<path
|
||||||
|
d="M 0,515.86 H 556.007 V 0 H 0 Z"
|
||||||
|
transform="translate(-281.97393,-97.5947)"
|
||||||
|
id="path57" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath59">
|
||||||
|
<path
|
||||||
|
d="M 0,515.86 H 556.007 V 0 H 0 Z"
|
||||||
|
transform="translate(-347.61213,-97.5947)"
|
||||||
|
id="path59" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath61">
|
||||||
|
<path
|
||||||
|
d="M 0,515.86 H 556.007 V 0 H 0 Z"
|
||||||
|
transform="translate(-2.5000001e-5)"
|
||||||
|
id="path61" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#000000"
|
||||||
|
borderopacity="0.25"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1">
|
||||||
|
<inkscape:page
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
inkscape:label="3"
|
||||||
|
id="page40"
|
||||||
|
width="741.34265"
|
||||||
|
height="687.81335"
|
||||||
|
margin="0"
|
||||||
|
bleed="0" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<g
|
||||||
|
id="layer-MC0"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
inkscape:label="fondo"
|
||||||
|
transform="translate(-1965.0467)">
|
||||||
|
<path
|
||||||
|
id="path41"
|
||||||
|
d="M 0,0 H 556.007 V 515.86 H 0 Z"
|
||||||
|
style="fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,1965.0467,687.81333)" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="layer-MC1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
inkscape:label="info"
|
||||||
|
transform="translate(-1965.0467)">
|
||||||
|
<path
|
||||||
|
id="path42"
|
||||||
|
d="m 0,0 c -27.777,0 -50.295,22.518 -50.295,50.295 0,27.777 22.518,50.295 50.295,50.295 27.777,0 50.295,-22.518 50.295,-50.295 C 50.295,22.518 27.777,0 0,0 m 0,161.68 c -61.516,0 -111.385,-49.869 -111.385,-111.385 0,-61.516 49.869,-111.385 111.385,-111.385 61.517,0 111.385,49.869 111.385,111.385 C 111.385,111.811 61.517,161.68 0,161.68"
|
||||||
|
style="fill:#29377c;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,2335.7179,344.41947)"
|
||||||
|
clip-path="url(#clipPath43)" />
|
||||||
|
<path
|
||||||
|
id="path44"
|
||||||
|
d="m 0,0 c -27.777,0 -50.295,22.518 -50.295,50.295 0,27.777 22.518,50.295 50.295,50.295 27.777,0 50.295,-22.518 50.295,-50.295 C 50.295,22.518 27.777,0 0,0 m 0,153.912 c -57.226,0 -103.617,-46.391 -103.617,-103.617 0,-57.226 46.391,-103.617 103.617,-103.617 57.226,0 103.617,46.391 103.617,103.617 0,57.226 -46.391,103.617 -103.617,103.617"
|
||||||
|
style="fill:#46a0de;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,2335.7179,344.41947)"
|
||||||
|
clip-path="url(#clipPath45)" />
|
||||||
|
<path
|
||||||
|
id="path46"
|
||||||
|
d="m 0,0 c -3.109,6.537 -4.853,13.849 -4.853,21.569 0,7.729 1.747,15.047 4.862,21.59 7.326,24.212 29.478,42.277 74.197,42.277 8.439,0 16.581,-1.262 24.252,-3.605 2.724,-0.831 4.536,2.798 2.221,4.456 -17.261,12.362 -38.405,19.644 -61.255,19.644 -54.683,0 -99.628,-41.658 -104.852,-94.968 5.343,-56.539 52.932,-100.779 110.87,-100.779 18.079,0 35.136,4.335 50.234,11.979 -6.849,-1.829 -14.044,-2.81 -21.47,-2.81 C 42.946,-80.647 9.739,-40.708 0,0"
|
||||||
|
style="fill:#9c135e;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,2275.1288,306.118)"
|
||||||
|
clip-path="url(#clipPath47)" />
|
||||||
|
<path
|
||||||
|
id="path48"
|
||||||
|
d="m 0,0 c 0,8.188 -1.537,16.013 -4.312,23.224 -0.146,0.381 -0.295,0.76 -0.448,1.137 -0.218,0.544 -0.452,1.079 -0.683,1.616 -0.724,1.677 -1.49,3.333 -2.344,4.936 -0.005,-0.029 -0.011,-0.058 -0.015,-0.087 -10.954,20.173 -32.324,33.869 -56.893,33.869 -5.355,0 -10.554,-0.663 -15.531,-1.89 16.618,-8.749 27.75,-26.517 26.854,-46.801 -1.143,-25.894 -22.283,-46.94 -48.182,-47.976 -20.408,-0.816 -38.239,10.541 -46.857,27.392 -9.913,13.159 -15.795,29.525 -15.795,47.27 0,5.351 0.54,10.575 1.562,15.624 0.532,2.628 -2.878,4.12 -4.503,1.988 -12.623,-16.564 -19.832,-37.474 -18.957,-60.104 1.889,-48.834 41.94,-88.287 90.797,-89.481 45.027,-1.102 83.018,29.749 92.971,71.479 0.327,1.369 0.614,2.754 0.878,4.146 0.693,3.22 1.148,6.527 1.342,9.905 0.011,0.141 0.029,0.28 0.04,0.421 C -0.032,-2.335 0,-1.335 0,-0.327 0,-0.278 -0.005,-0.23 -0.005,-0.181 -0.005,-0.12 0,-0.061 0,0"
|
||||||
|
style="fill:#6bc62f;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,2473.8735,301.73493)"
|
||||||
|
clip-path="url(#clipPath49)" />
|
||||||
|
<path
|
||||||
|
id="path50"
|
||||||
|
d="M 0,0 V 56.239 H 38.554 V 46.726 H 11.355 V 33.413 H 34.833 V 23.899 H 11.355 V 0 Z"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,2120.1973,557.68707)"
|
||||||
|
clip-path="url(#clipPath51)" />
|
||||||
|
<path
|
||||||
|
id="path52"
|
||||||
|
d="m 0,0 c 0,-6.368 1.471,-11.196 4.412,-14.481 2.94,-3.288 6.675,-4.93 11.202,-4.93 4.526,0 8.241,1.63 11.143,4.89 2.903,3.262 4.355,8.153 4.355,14.674 0,6.445 -1.413,11.253 -4.239,14.425 -2.826,3.171 -6.579,4.757 -11.259,4.757 -4.681,0 -8.454,-1.606 -11.317,-4.814 C 1.432,11.31 0,6.47 0,0 m -11.701,-0.384 c 0,5.729 0.857,10.537 2.571,14.425 1.278,2.864 3.024,5.434 5.237,7.711 2.211,2.275 4.635,3.963 7.269,5.064 3.504,1.482 7.544,2.224 12.122,2.224 8.287,0 14.917,-2.57 19.892,-7.71 4.973,-5.141 7.461,-12.29 7.461,-21.446 0,-9.079 -2.469,-16.183 -7.404,-21.31 -4.936,-5.127 -11.534,-7.692 -19.795,-7.692 -8.363,0 -15.013,2.552 -19.949,7.655 -4.936,5.101 -7.404,12.128 -7.404,21.079"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,2196.6155,520.14307)"
|
||||||
|
clip-path="url(#clipPath53)" />
|
||||||
|
<path
|
||||||
|
id="path54"
|
||||||
|
d="m 0,0 h 8.401 c 5.448,0 8.849,0.23 10.205,0.691 1.355,0.46 2.417,1.253 3.184,2.378 0.767,1.125 1.151,2.532 1.151,4.22 0,1.892 -0.506,3.419 -1.515,4.584 -1.011,1.164 -2.437,1.9 -4.278,2.207 -0.921,0.126 -3.683,0.191 -8.286,0.191 H 0 Z m -11.355,-32.455 v 56.24 h 23.9 c 6.009,0 10.376,-0.506 13.101,-1.515 2.723,-1.011 4.903,-2.807 6.54,-5.39 1.636,-2.584 2.455,-5.538 2.455,-8.863 0,-4.218 -1.241,-7.704 -3.721,-10.453 -2.481,-2.75 -6.19,-4.483 -11.125,-5.199 2.455,-1.432 4.482,-3.004 6.081,-4.718 1.598,-1.713 3.753,-4.757 6.464,-9.13 l 6.866,-10.972 h -13.58 l -8.209,12.237 c -2.916,4.375 -4.911,7.13 -5.985,8.268 -1.074,1.138 -2.212,1.919 -3.414,2.34 -1.203,0.423 -3.108,0.633 -5.716,0.633 H 0 v -23.478 z"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,2280.7572,514.41427)"
|
||||||
|
clip-path="url(#clipPath55)" />
|
||||||
|
<path
|
||||||
|
id="path56"
|
||||||
|
d="M 0,0 V 56.239 H 16.995 L 27.199,17.877 37.288,56.239 H 54.321 V 0 H 43.772 V 44.27 L 32.608,0 H 21.675 L 10.55,44.27 V 0 Z"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,2341.0119,557.68707)"
|
||||||
|
clip-path="url(#clipPath57)" />
|
||||||
|
<path
|
||||||
|
id="path58"
|
||||||
|
d="M 0,0 V 56.239 H 11.355 V 34.105 h 22.25 V 56.239 H 44.96 V 0 H 33.605 V 24.591 H 11.355 V 0 Z"
|
||||||
|
style="fill:#6bc62f;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,2428.5295,557.68707)"
|
||||||
|
clip-path="url(#clipPath59)" />
|
||||||
|
<path
|
||||||
|
id="path60"
|
||||||
|
d="m 424.145,154.792 h 9.284 v -9.245 h -9.284 z m 2.571,-37.632 c -1.381,-0.461 -3.568,-1.011 -6.56,-1.651 -2.993,-0.639 -4.948,-1.265 -5.869,-1.879 -1.408,-0.997 -2.111,-2.264 -2.111,-3.798 0,-1.509 0.563,-2.814 1.688,-3.912 1.125,-1.101 2.558,-1.65 4.297,-1.65 1.943,0 3.798,0.639 5.562,1.918 1.305,0.972 2.161,2.16 2.571,3.568 0.281,0.921 0.422,2.672 0.422,5.255 z m -19.067,37.632 h 9.246 v -9.245 h -9.246 z m 4.643,-28.886 -9.783,1.765 c 1.1,3.938 2.992,6.854 5.677,8.747 2.686,1.892 6.676,2.838 11.97,2.838 4.808,0 8.388,-0.57 10.741,-1.707 2.353,-1.138 4.009,-2.583 4.968,-4.334 0.959,-1.753 1.439,-4.969 1.439,-9.648 l -0.115,-12.583 c 0,-3.582 0.173,-6.222 0.517,-7.922 0.346,-1.702 0.991,-3.524 1.938,-5.467 h -10.665 c -0.281,0.716 -0.627,1.776 -1.035,3.183 -0.181,0.639 -0.307,1.062 -0.385,1.267 -1.842,-1.791 -3.811,-3.134 -5.907,-4.029 -2.098,-0.894 -4.335,-1.342 -6.714,-1.342 -4.194,0 -7.499,1.137 -9.917,3.415 -2.416,2.275 -3.624,5.153 -3.624,8.631 0,2.302 0.549,4.354 1.649,6.157 1.1,1.803 2.64,3.184 4.623,4.144 1.981,0.958 4.84,1.796 8.574,2.512 5.038,0.946 8.528,1.828 10.473,2.648 v 1.074 c 0,2.07 -0.513,3.547 -1.534,4.43 -1.025,0.882 -2.955,1.323 -5.794,1.323 -1.918,0 -3.413,-0.378 -4.487,-1.132 -1.074,-0.753 -1.946,-2.077 -2.609,-3.97"
|
||||||
|
style="fill:#46a0de;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,1965.0467,687.81333)"
|
||||||
|
clip-path="url(#clipPath61)" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 11 KiB |
BIN
static/y_img/other/no_img - copia.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
static/y_img/other/no_img.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
static/y_img/other/no_img_6.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
BIN
static/y_img/solutions/cambio.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
static/y_img/solutions/capacitacion.png
Normal file
After Width: | Height: | Size: 7.9 KiB |
BIN
static/y_img/solutions/girl.png
Normal file
After Width: | Height: | Size: 610 KiB |
BIN
static/y_img/solutions/liderazgo.png
Normal file
After Width: | Height: | Size: 7.9 KiB |
BIN
static/y_img/solutions/objetivos.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
static/y_img/solutions/reclutamiento_seleccion.png
Normal file
After Width: | Height: | Size: 12 KiB |
34
static/z_comps/notify.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// https://simple-notify.github.io/simple-notify/
|
||||||
|
// https://github.com/simple-notify/simple-notify/tree/master
|
||||||
|
|
||||||
|
|
||||||
|
// const btn = document.querySelector('#btn')
|
||||||
|
/**
|
||||||
|
* Muestra una notificación personalizada
|
||||||
|
* @param {string} title - Título de la notificación
|
||||||
|
* @param {string} body - Cuerpo/Contenido de la notificación
|
||||||
|
* @param {'error'|'warning'|'success'|'info'} [typeAlert='info'] - Tipo de notificación
|
||||||
|
*/
|
||||||
|
function simpleNotification(title, body, typeAlert, autotimeout=8000){
|
||||||
|
new Notify ({
|
||||||
|
// 'error', 'warning', 'success', 'info'
|
||||||
|
status: typeAlert,
|
||||||
|
title: title,
|
||||||
|
text: body,
|
||||||
|
effect: 'fade',
|
||||||
|
speed: 500,
|
||||||
|
customClass: '',
|
||||||
|
customIcon: '',
|
||||||
|
showIcon: true,
|
||||||
|
showCloseButton: true,
|
||||||
|
autoclose: true,
|
||||||
|
autotimeout: autotimeout,
|
||||||
|
notificationsGap: null,
|
||||||
|
notificationsPadding: null,
|
||||||
|
type: 'outline',
|
||||||
|
position: 'x-center top',
|
||||||
|
customWrapper: '',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export { simpleNotification };
|
16
static/z_comps/save_exp_timestamp.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
|
||||||
|
// validar que exista el item exp
|
||||||
|
if (!localStorage.getItem("exp")) {
|
||||||
|
localStorage.setItem('exp', JSON.stringify({ exp: exp, min15: true, min10: true, min2: true }));
|
||||||
|
// en caso de exista validar que el elemento guardado el timestamp guardado en el front = timestamp enviado por el backend
|
||||||
|
} else {
|
||||||
|
let tmstmp = parseInt(JSON.parse(localStorage.getItem("exp"))['exp'])
|
||||||
|
if (tmstmp != exp) {
|
||||||
|
localStorage.setItem('exp', JSON.stringify({ exp: exp, min15: true, min10: true, min2: true }));
|
||||||
|
// console.log("el elemento se actualizo")
|
||||||
|
}
|
||||||
|
// else {
|
||||||
|
// // console.log("tl timestamp no ha sido cambiado")
|
||||||
|
// }
|
||||||
|
}
|
45
static/z_comps/validation_timestamp.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { simpleNotification } from './notify.js'
|
||||||
|
|
||||||
|
|
||||||
|
function validarExpiracion() {
|
||||||
|
let timestampActual = Math.floor(Date.now() / 1000); // Timestamp en segundos
|
||||||
|
let jsonData = JSON.parse(localStorage.getItem('exp'));
|
||||||
|
let expiration = parseInt(jsonData?.exp); // Usamos encadenamiento opcional
|
||||||
|
|
||||||
|
if (isNaN(expiration)) {
|
||||||
|
console.warn("No se encontró un valor válido para 'exp' en localStorage.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tiempoRestante = expiration - timestampActual;
|
||||||
|
let minutosRestantes = (tiempoRestante / 60).toFixed(0); // 1 decimal
|
||||||
|
|
||||||
|
let actualizado = false;
|
||||||
|
let txt_min = '';
|
||||||
|
|
||||||
|
if (tiempoRestante < 1020 && tiempoRestante > 780 && jsonData.min15) {
|
||||||
|
txt_min = minutosRestantes;
|
||||||
|
jsonData.min15 = false;
|
||||||
|
actualizado = true;
|
||||||
|
} else if (tiempoRestante < 660 && tiempoRestante > 420 && jsonData.min10) {
|
||||||
|
txt_min = minutosRestantes;
|
||||||
|
jsonData.min10 = false;
|
||||||
|
actualizado = true;
|
||||||
|
} else if (tiempoRestante < 300 && tiempoRestante > 60 && jsonData.min2) {
|
||||||
|
txt_min = minutosRestantes;
|
||||||
|
jsonData.min2 = false;
|
||||||
|
actualizado = true;
|
||||||
|
} else if (tiempoRestante <= 0) {
|
||||||
|
window.location.href = "/logout";
|
||||||
|
return; // Importante para que no se ejecute el setItem innecesariamente
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actualizado) {
|
||||||
|
localStorage.setItem('exp', JSON.stringify(jsonData));
|
||||||
|
simpleNotification('⚠️ !Atención¡ ⚠️', `La sesión expirará en ~${txt_min} minutos`, 'warning', 30000)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ejecutar la validación cada 5 segundos | 1 seg = 1000 milisegundos | 1 min = 60,000 milisegundos
|
||||||
|
setInterval(validarExpiracion, 5000); //180,000 = 3 minutos
|
116
templates/a_home/home.html
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
{% extends 'template.html' %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
<link rel="stylesheet" href="{{url_for('static', filename='a_home/home.css')}}">
|
||||||
|
{% endblock css %}
|
||||||
|
|
||||||
|
{% block navbar %}
|
||||||
|
{% include 'z_comps/navbar.html' %}
|
||||||
|
{% endblock navbar %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
<div class="r-i">
|
||||||
|
<div class="r-i-ii">
|
||||||
|
<spam>
|
||||||
|
Potenciamos el talento con soluciones personalizadas que alinean personas, información y propósito. Diseñamos
|
||||||
|
estrategias basadas en información, ordenada, confiable, disponible y segura, dando FORMHä a tus decisiones.
|
||||||
|
</spam>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="r-ii">
|
||||||
|
<!-- Tarjeta 1 -->
|
||||||
|
<div class="card-flyer">
|
||||||
|
<div class="image-box">
|
||||||
|
<img src="{{url_for('static', filename='y_img/home/software.avif')}}" alt="Software">
|
||||||
|
</div>
|
||||||
|
<div class="text-container">
|
||||||
|
<h6>
|
||||||
|
Actualización del proceso de reclutamiento y selección.
|
||||||
|
</h6>
|
||||||
|
<p>
|
||||||
|
Actualmente desde la AI hasta las técnicas de entrevista, han abierto un nuevo horizonte en el marco del
|
||||||
|
reclutamiento y selección de personal…
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tarjeta 2 -->
|
||||||
|
<div class="card-flyer">
|
||||||
|
<div class="image-box">
|
||||||
|
<img src="{{url_for('static', filename='y_img/home/work.avif')}}" alt="Productividad">
|
||||||
|
</div>
|
||||||
|
<div class="text-container">
|
||||||
|
<h6>
|
||||||
|
Beneficios e impacto en la productividad.
|
||||||
|
</h6>
|
||||||
|
<p>
|
||||||
|
En la medida que adoptemos la NOM 035, factores de riesgos psicosocial en el trabajo, identificación y
|
||||||
|
prevención; como una guía para optimizar el clima y…
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tarjeta 3 -->
|
||||||
|
<div class="card-flyer">
|
||||||
|
<div class="image-box">
|
||||||
|
<img src="{{url_for('static', filename='y_img/home/chair.avif')}}" alt="Ley Silla">
|
||||||
|
</div>
|
||||||
|
<div class="text-container">
|
||||||
|
<h6>
|
||||||
|
Alcances y objetivos de la Ley Silla.
|
||||||
|
</h6>
|
||||||
|
<p>
|
||||||
|
El pasado 19 de diciembre de 2024, se publico en el Diario Oficial de la Federación el decreto por el cual
|
||||||
|
se reforman y adicionan diversas…
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div id="carouselExampleCaptions" class="carousel slide" data-bs-ride="carousel">
|
||||||
|
<div class="carousel-indicators">
|
||||||
|
<button type="button" data-bs-target="#carouselExampleCaptions" data-bs-slide-to="0" class="active" aria-current="true" aria-label="Slide 1"></button>
|
||||||
|
<button type="button" data-bs-target="#carouselExampleCaptions" data-bs-slide-to="1" aria-label="Slide 2"></button>
|
||||||
|
<button type="button" data-bs-target="#carouselExampleCaptions" data-bs-slide-to="2" aria-label="Slide 3"></button>
|
||||||
|
</div>
|
||||||
|
<div class="carousel-inner">
|
||||||
|
<div class="carousel-item active">
|
||||||
|
<img src="{{url_for('static', filename='y_img/home/software.avif')}}" class="d-block w-100" alt="...">
|
||||||
|
<div class="carousel-caption d-none d-md-block">
|
||||||
|
<h5>First slide label</h5>
|
||||||
|
<p>Some representative placeholder content for the first slide.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="carousel-item">
|
||||||
|
<img src="{{url_for('static', filename='y_img/home/work.avif')}}" class="d-block w-100" alt="...">
|
||||||
|
<div class="carousel-caption d-none d-md-block">
|
||||||
|
<h5>Second slide label</h5>
|
||||||
|
<p>Some representative placeholder content for the second slide.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="carousel-item">
|
||||||
|
<img src="{{url_for('static', filename='y_img/home/chair.avif')}}" class="d-block w-100" alt="...">
|
||||||
|
<div class="carousel-caption d-none d-md-block">
|
||||||
|
<h5>Third slide label</h5>
|
||||||
|
<p>Some representative placeholder content for the third slide.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="carousel-control-prev" type="button" data-bs-target="#carouselExampleCaptions" data-bs-slide="prev">
|
||||||
|
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
|
||||||
|
<span class="visually-hidden">Previous</span>
|
||||||
|
</button>
|
||||||
|
<button class="carousel-control-next" type="button" data-bs-target="#carouselExampleCaptions" data-bs-slide="next">
|
||||||
|
<span class="carousel-control-next-icon" aria-hidden="true"></span>
|
||||||
|
<span class="visually-hidden">Next</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock body %}
|
||||||
|
|
@ -1,11 +1,11 @@
|
|||||||
{% extends 'template.html' %}
|
{% extends 'template.html' %}
|
||||||
|
|
||||||
{% block css %}
|
{% block css %}
|
||||||
<link rel="stylesheet" href="{{url_for('static', filename='about-us/about-us.css')}}">
|
<link rel="stylesheet" href="{{url_for('static', filename='b_about-us/about-us.css')}}">
|
||||||
{% endblock css %}
|
{% endblock css %}
|
||||||
|
|
||||||
{% block navbar %}
|
{% block navbar %}
|
||||||
{% include 'comps/navbar.html' %}
|
{% include 'z_comps/navbar.html' %}
|
||||||
{% endblock navbar %}
|
{% endblock navbar %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
@ -13,21 +13,21 @@
|
|||||||
|
|
||||||
<div class="expanding-panels">
|
<div class="expanding-panels">
|
||||||
<!-- Quiénes Somos -->
|
<!-- Quiénes Somos -->
|
||||||
<div class="panel p1">
|
<div class="panel p1" data-aos="zoom-in" data-aos-delay="0" data-aos-duration="800" data-aos-easing="ease-in-out">
|
||||||
<div class="panel-content">
|
<div class="panel-content">
|
||||||
<div class="panel-title">
|
<div class="panel-title">
|
||||||
<h3>¿Quiénes Somos?</h3>
|
<h3>¿Quiénes Somos?</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-text">
|
<div class="panel-text">
|
||||||
<p>Somos Cecilia y Enrique, profesionistas en Capital Humano y Desarrollo Organizacional, con más de 20 años de experiencia impulsando el crecimiento de empresas a través de estrategias de talento 360º.</p>
|
<p>Somos Cecilia y Enrique, profesionistas en Capital Humano y Desarrollo Organizacional, con más de 20 años de experiencia impulsando el crecimiento de empresas a través de estrategias de talento 360º.</p>
|
||||||
<p>Fundamos {% include 'comps/forma.html' %} para transformar la gestión del talento con soluciones personalizadas, basadas en datos confiables y estrategias innovadoras. A través de alianzas estratégicas y nuestras propias iniciativas, ayudamos a las empresas a tomar decisiones informadas, optimizando su capital humano con herramientas efectivas y de alto impacto.</p>
|
<p>Fundamos {% include 'z_comps/formha.html' %} para transformar la gestión del talento con soluciones personalizadas, basadas en datos confiables y estrategias innovadoras. A través de alianzas estratégicas y nuestras propias iniciativas, ayudamos a las empresas a tomar decisiones informadas, optimizando su capital humano con herramientas efectivas y de alto impacto.</p>
|
||||||
<p>Con {% include 'comps/forma.html' %}, tu equipo no solo crece, sino que evoluciona.</p>
|
<p>Con {% include 'z_comps/formha.html' %}, tu equipo no solo crece, sino que evoluciona.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Misión -->
|
<!-- Misión -->
|
||||||
<div class="panel p2">
|
<div class="panel p2" data-aos="zoom-in" data-aos-delay="250" data-aos-duration="800" data-aos-easing="ease-in-out">
|
||||||
<div class="panel-content">
|
<div class="panel-content">
|
||||||
<div class="panel-title">
|
<div class="panel-title">
|
||||||
<h3>Misión</h3>
|
<h3>Misión</h3>
|
||||||
@ -40,7 +40,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Visión -->
|
<!-- Visión -->
|
||||||
<div class="panel p3">
|
<div class="panel p3" data-aos="zoom-in" data-aos-delay="450" data-aos-duration="800" data-aos-easing="ease-in-out">
|
||||||
<div class="panel-content">
|
<div class="panel-content">
|
||||||
<div class="panel-title">
|
<div class="panel-title">
|
||||||
<h3>Visión</h3>
|
<h3>Visión</h3>
|
||||||
@ -53,7 +53,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Valores -->
|
<!-- Valores -->
|
||||||
<div class="panel p4">
|
<div class="panel p4" data-aos="zoom-in" data-aos-delay="650" data-aos-duration="800" data-aos-easing="ease-in-out">
|
||||||
<div class="panel-content">
|
<div class="panel-content">
|
||||||
<div class="panel-title">
|
<div class="panel-title">
|
||||||
<h3>Valores</h3>
|
<h3>Valores</h3>
|
||||||
@ -73,6 +73,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{% endblock body %}
|
{% endblock body %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
|
||||||
|
<!-- {# aos script #} -->
|
||||||
|
{% include 'z_comps/aos_script.html' %}
|
||||||
|
|
||||||
|
{% endblock js %}
|
@ -2,37 +2,53 @@
|
|||||||
|
|
||||||
{% block css %}
|
{% block css %}
|
||||||
<!-- {# modal bootstrap #} -->
|
<!-- {# modal bootstrap #} -->
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css">
|
<link rel="stylesheet" href="{{url_for('static', filename='c_solutions/solutions.css')}}">
|
||||||
<link rel="stylesheet" href="{{url_for('static', filename='solutions/solutions.css')}}">
|
|
||||||
{% endblock css %}
|
{% endblock css %}
|
||||||
|
|
||||||
{% block navbar %}
|
{% block navbar %}
|
||||||
{% include 'comps/navbar.html' %}
|
{% include 'z_comps/navbar.html' %}
|
||||||
{% endblock navbar %}
|
{% endblock navbar %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
|
||||||
<div class="parent">
|
<!-- <div class="parent">
|
||||||
<div class="i-i" data-toggle="modal" data-target="#forma-reclutamiento"></div>
|
<div class="i-i" data-toggle="modal" data-target="#forma-reclutamiento"></div>
|
||||||
<div class="i-ii" data-toggle="modal" data-target="#forma-liderazgo"></div>
|
<div class="i-ii" data-toggle="modal" data-target="#forma-liderazgo"></div>
|
||||||
<div class="i-iii" data-toggle="modal" data-target="#forma-capacitacion"></div>
|
<div class="i-iii" data-toggle="modal" data-target="#forma-capacitacion"></div>
|
||||||
<div class="ii-i" data-toggle="modal" data-target="#forma-cambio"></div>
|
<div class="ii-i" data-toggle="modal" data-target="#forma-cambio"></div>
|
||||||
<div class="ii-ii" data-toggle="modal" data-target="#forma-objetivos"></div>
|
<div class="ii-ii" data-toggle="modal" data-target="#forma-objetivos"></div>
|
||||||
|
</div> -->
|
||||||
|
|
||||||
|
<div class="parent">
|
||||||
|
<div class="i-i" data-toggle="modal" data-target="#forma-reclutamiento"
|
||||||
|
data-aos="zoom-in" data-aos-delay="0" data-aos-duration="800"></div>
|
||||||
|
|
||||||
|
<div class="i-ii" data-toggle="modal" data-target="#forma-liderazgo"
|
||||||
|
data-aos="zoom-in" data-aos-delay="200" data-aos-duration="800"></div>
|
||||||
|
|
||||||
|
<div class="i-iii" data-toggle="modal" data-target="#forma-capacitacion"
|
||||||
|
data-aos="zoom-in" data-aos-delay="300" data-aos-duration="800"></div>
|
||||||
|
|
||||||
|
<div class="ii-i" data-toggle="modal" data-target="#forma-cambio"
|
||||||
|
data-aos="zoom-in" data-aos-delay="400" data-aos-duration="800"></div>
|
||||||
|
|
||||||
|
<div class="ii-ii" data-toggle="modal" data-target="#forma-objetivos"
|
||||||
|
data-aos="zoom-in" data-aos-delay="500" data-aos-duration="800"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- da forma reclutamiento -->
|
<!-- da forma reclutamiento -->
|
||||||
<div class="modal fade" id="forma-reclutamiento" tabindex="-1" role="dialog" aria-labelledby="forma-reclutamientoTitle" aria-hidden="true">
|
<div class="modal fade" id="forma-reclutamiento" tabindex="-1" role="dialog" aria-labelledby="forma-reclutamientoTitle" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
|
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content" style="border: 6px solid #90b83b;">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="exampleModalLongTitle">Impulsa tu equipo con un reclutamiento más preciso, ágil y estratégico.</h5>
|
<h5 class="modal-title" id="exampleModalLongTitle">Impulsa tu equipo con un reclutamiento más preciso, ágil y estratégico.</h5>
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
<!-- <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> -->
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p>
|
<p>
|
||||||
En {% include 'comps/forma.html' %}, combinamos tecnología y metodologías avanzadas para atraer, identificar y seleccionar talento de alto
|
En {% include 'z_comps/formha.html' %}, combinamos tecnología y metodologías avanzadas para atraer, identificar y seleccionar talento de alto
|
||||||
potencial.
|
potencial.
|
||||||
<ul>
|
<ul>
|
||||||
<li><b>Plataformas digitales e inteligencia artificial:</b> Utilizamos herramientas especializadas, para optimizar
|
<li><b>Plataformas digitales e inteligencia artificial:</b> Utilizamos herramientas especializadas, para optimizar
|
||||||
@ -50,9 +66,9 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cerrar</button>
|
||||||
</div> -->
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -60,17 +76,15 @@
|
|||||||
<!-- da forma a tu liderazgo-->
|
<!-- da forma a tu liderazgo-->
|
||||||
<div class="modal fade" id="forma-liderazgo" tabindex="-1" role="dialog" aria-labelledby="forma-liderazgoTitle" aria-hidden="true">
|
<div class="modal fade" id="forma-liderazgo" tabindex="-1" role="dialog" aria-labelledby="forma-liderazgoTitle" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
|
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content" style="border: 6px solid #8c1f5a;">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="exampleModalLongTitle">Da {% include 'comps/forma.html' %} a tu liderazgo.</h5>
|
<h5 class="modal-title" id="exampleModalLongTitle">Da {% include 'z_comps/formha.html' %} a tu liderazgo.</h5>
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
<!-- <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> -->
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p>
|
<p>
|
||||||
Prepara a tus líderes para afrontar los desafíos del entorno empresarial actual con estrategias que potencian su
|
Prepara a tus líderes para afrontar los desafíos del entorno empresarial actual con estrategias que potencian su
|
||||||
crecimiento y efectividad. En {% include 'comps/forma.html' %}, diseñamos soluciones para identificar, desarrollar y fortalecer el liderazgo
|
crecimiento y efectividad. En {% include 'z_comps/formha.html' %}, diseñamos soluciones para identificar, desarrollar y fortalecer el liderazgo
|
||||||
en todos los niveles de la organización. <br>
|
en todos los niveles de la organización. <br>
|
||||||
Nos especializamos en:
|
Nos especializamos en:
|
||||||
<ul>
|
<ul>
|
||||||
@ -82,9 +96,9 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cerrar</button>
|
||||||
</div> -->
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -92,12 +106,10 @@
|
|||||||
<!-- da forma a tu capacitación-->
|
<!-- da forma a tu capacitación-->
|
||||||
<div class="modal fade" id="forma-capacitacion" tabindex="-1" role="dialog" aria-labelledby="forma-capacitacionTitle" aria-hidden="true">
|
<div class="modal fade" id="forma-capacitacion" tabindex="-1" role="dialog" aria-labelledby="forma-capacitacionTitle" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
|
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content" style="border: 6px solid #5d9dd1;">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="exampleModalLongTitle">Da {% include 'comps/forma.html' %} a tu capacitación. </h5>
|
<h5 class="modal-title" id="exampleModalLongTitle">Da {% include 'z_comps/formha.html' %} a tu capacitación. </h5>
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
<!-- <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> -->
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div>
|
<div>
|
||||||
@ -133,9 +145,9 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cerrar</button>
|
||||||
</div> -->
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -143,12 +155,10 @@
|
|||||||
<!-- da forma al cambio-->
|
<!-- da forma al cambio-->
|
||||||
<div class="modal fade" id="forma-cambio" tabindex="-1" role="dialog" aria-labelledby="forma-cambioTitle" aria-hidden="true">
|
<div class="modal fade" id="forma-cambio" tabindex="-1" role="dialog" aria-labelledby="forma-cambioTitle" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
|
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content" style="border: 6px solid #002e72;">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="exampleModalLongTitle">Simplifica, optimiza y fortalece tu capital humano con asesoría especializada.</h5>
|
<h5 class="modal-title" id="exampleModalLongTitle">Simplifica, optimiza y fortalece tu capital humano con asesoría especializada.</h5>
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
<!-- <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> -->
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p>
|
<p>
|
||||||
@ -164,9 +174,9 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cerrar</button>
|
||||||
</div> -->
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -174,12 +184,10 @@
|
|||||||
<!-- da forma a tus objetivos-->
|
<!-- da forma a tus objetivos-->
|
||||||
<div class="modal fade" id="forma-objetivos" tabindex="-1" role="dialog" aria-labelledby="forma-objetivosTitle" aria-hidden="true">
|
<div class="modal fade" id="forma-objetivos" tabindex="-1" role="dialog" aria-labelledby="forma-objetivosTitle" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
|
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content" style="border: 6px solid #707272;">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="exampleModalLongTitle">Convierte datos en decisiones estratégicas para impulsar el crecimiento de tu empresa</h5>
|
<h5 class="modal-title" id="exampleModalLongTitle">Convierte datos en decisiones estratégicas para impulsar el crecimiento de tu empresa</h5>
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
<!-- <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> -->
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p>
|
<p>
|
||||||
@ -193,9 +201,9 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cerrar</button>
|
||||||
</div> -->
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -203,8 +211,13 @@
|
|||||||
{% endblock body %}
|
{% endblock body %}
|
||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
<!-- 2. Al final del BODY (en este orden) -->
|
<!-- {# modal bootstrap #} -->
|
||||||
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
|
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
|
||||||
<!-- <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script> -->
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.min.js"></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- {# aos script #} -->
|
||||||
|
{% include 'z_comps/aos_script.html' %}
|
||||||
|
|
||||||
{% endblock js %}
|
{% endblock js %}
|
@ -1,73 +0,0 @@
|
|||||||
<style>
|
|
||||||
/* Estilos personalizados para el footer */
|
|
||||||
.footer {
|
|
||||||
background-color: #50164A ; /* Color de fondo oscuro */
|
|
||||||
color: #ffffff; /* Color del texto */
|
|
||||||
padding: 20px 0;
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<!-- Footer -->
|
|
||||||
<footer class="footer">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
|
|
||||||
<!-- Columna 1: Información de contacto -->
|
|
||||||
<div class="col-md-6 text-center">
|
|
||||||
<h5>Contacto</h5>
|
|
||||||
<button type="button" class="btn btn-light">
|
|
||||||
<i class="bi bi-telephone-fill"></i> +52 5480-xxxx
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn btn-light">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" style="color: #000000;" width="16" height="16" fill="currentColor" class="bi bi-envelope-fill" viewBox="0 0 16 16">
|
|
||||||
<path d="M.05 3.555A2 2 0 0 1 2 2h12a2 2 0 0 1 1.95 1.555L8 8.414zM0 4.697v7.104l5.803-3.558zM6.761 8.83l-6.57 4.027A2 2 0 0 0 2 14h12a2 2 0 0 0 1.808-1.144l-6.57-4.027L8 9.586zm3.436-.586L16 11.801V4.697z"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Columna 2: Enlaces útiles -->
|
|
||||||
<div class="col-md-6 text-center">
|
|
||||||
<h5>Redes Sociales</h5>
|
|
||||||
<div class="r-i-i">
|
|
||||||
<!-- <button onclick="window.location.href='{{ url_for('home') }}'"> </button> -->
|
|
||||||
<button type="button" class="btn btn-light">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" style="color: #3b5998;" width="16" height="16" fill="currentColor" class="bi bi-facebook" viewBox="0 0 16 16">
|
|
||||||
<path d="M16 8.049c0-4.446-3.582-8.05-8-8.05C3.58 0-.002 3.603-.002 8.05c0 4.017 2.926 7.347 6.75 7.951v-5.625h-2.03V8.05H6.75V6.275c0-2.017 1.195-3.131 3.022-3.131.876 0 1.791.157 1.791.157v1.98h-1.009c-.993 0-1.303.621-1.303 1.258v1.51h2.218l-.354 2.326H9.25V16c3.824-.604 6.75-3.934 6.75-7.951"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn btn-light">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" style="color: #000000;" width="16" height="16" fill="currentColor" class="bi bi-twitter-x" viewBox="0 0 16 16">
|
|
||||||
<path d="M12.6.75h2.454l-5.36 6.142L16 15.25h-4.937l-3.867-5.07-4.425 5.07H.316l5.733-6.57L0 .75h5.063l3.495 4.633L12.601.75Zm-.86 13.028h1.36L4.323 2.145H2.865z"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn btn-light">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" style="color: #F56040;" width="16" height="16" fill="currentColor" class="bi bi-instagram" viewBox="0 0 16 16">
|
|
||||||
<path d="M8 0C5.829 0 5.556.01 4.703.048 3.85.088 3.269.222 2.76.42a3.9 3.9 0 0 0-1.417.923A3.9 3.9 0 0 0 .42 2.76C.222 3.268.087 3.85.048 4.7.01 5.555 0 5.827 0 8.001c0 2.172.01 2.444.048 3.297.04.852.174 1.433.372 1.942.205.526.478.972.923 1.417.444.445.89.719 1.416.923.51.198 1.09.333 1.942.372C5.555 15.99 5.827 16 8 16s2.444-.01 3.298-.048c.851-.04 1.434-.174 1.943-.372a3.9 3.9 0 0 0 1.416-.923c.445-.445.718-.891.923-1.417.197-.509.332-1.09.372-1.942C15.99 10.445 16 10.173 16 8s-.01-2.445-.048-3.299c-.04-.851-.175-1.433-.372-1.941a3.9 3.9 0 0 0-.923-1.417A3.9 3.9 0 0 0 13.24.42c-.51-.198-1.092-.333-1.943-.372C10.443.01 10.172 0 7.998 0zm-.717 1.442h.718c2.136 0 2.389.007 3.232.046.78.035 1.204.166 1.486.275.373.145.64.319.92.599s.453.546.598.92c.11.281.24.705.275 1.485.039.843.047 1.096.047 3.231s-.008 2.389-.047 3.232c-.035.78-.166 1.203-.275 1.485a2.5 2.5 0 0 1-.599.919c-.28.28-.546.453-.92.598-.28.11-.704.24-1.485.276-.843.038-1.096.047-3.232.047s-2.39-.009-3.233-.047c-.78-.036-1.203-.166-1.485-.276a2.5 2.5 0 0 1-.92-.598 2.5 2.5 0 0 1-.6-.92c-.109-.281-.24-.705-.275-1.485-.038-.843-.046-1.096-.046-3.233s.008-2.388.046-3.231c.036-.78.166-1.204.276-1.486.145-.373.319-.64.599-.92s.546-.453.92-.598c.282-.11.705-.24 1.485-.276.738-.034 1.024-.044 2.515-.045zm4.988 1.328a.96.96 0 1 0 0 1.92.96.96 0 0 0 0-1.92m-4.27 1.122a4.109 4.109 0 1 0 0 8.217 4.109 4.109 0 0 0 0-8.217m0 1.441a2.667 2.667 0 1 1 0 5.334 2.667 2.667 0 0 1 0-5.334"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn btn-light">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" style="color: #FF0000;" width="16" height="16" fill="currentColor" class="bi bi-youtube" viewBox="0 0 16 16">
|
|
||||||
<path d="M8.051 1.999h.089c.822.003 4.987.033 6.11.335a2.01 2.01 0 0 1 1.415 1.42c.101.38.172.883.22 1.402l.01.104.022.26.008.104c.065.914.073 1.77.074 1.957v.075c-.001.194-.01 1.108-.082 2.06l-.008.105-.009.104c-.05.572-.124 1.14-.235 1.558a2.01 2.01 0 0 1-1.415 1.42c-1.16.312-5.569.334-6.18.335h-.142c-.309 0-1.587-.006-2.927-.052l-.17-.006-.087-.004-.171-.007-.171-.007c-1.11-.049-2.167-.128-2.654-.26a2.01 2.01 0 0 1-1.415-1.419c-.111-.417-.185-.986-.235-1.558L.09 9.82l-.008-.104A31 31 0 0 1 0 7.68v-.123c.002-.215.01-.958.064-1.778l.007-.103.003-.052.008-.104.022-.26.01-.104c.048-.519.119-1.023.22-1.402a2.01 2.01 0 0 1 1.415-1.42c.487-.13 1.544-.21 2.654-.26l.17-.007.172-.006.086-.003.171-.007A100 100 0 0 1 7.858 2zM6.4 5.209v4.818l4.157-2.408z"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn btn-light">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" style="color: #0e76a8;" width="16" height="16" fill="currentColor" class="bi bi-linkedin" viewBox="0 0 16 16">
|
|
||||||
<path d="M0 1.146C0 .513.526 0 1.175 0h13.65C15.474 0 16 .513 16 1.146v13.708c0 .633-.526 1.146-1.175 1.146H1.175C.526 16 0 15.487 0 14.854zm4.943 12.248V6.169H2.542v7.225zm-1.2-8.212c.837 0 1.358-.554 1.358-1.248-.015-.709-.52-1.248-1.342-1.248S2.4 3.226 2.4 3.934c0 .694.521 1.248 1.327 1.248zm4.908 8.212V9.359c0-.216.016-.432.08-.586.173-.431.568-.878 1.232-.878.869 0 1.216.662 1.216 1.634v3.865h2.401V9.25c0-2.22-1.184-3.252-2.764-3.252-1.274 0-1.845.7-2.165 1.193v.025h-.016l.016-.025V6.169h-2.4c.03.678 0 7.225 0 7.225z"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Derechos de autor -->
|
|
||||||
<div class="row mt-4">
|
|
||||||
<div class="col-12 text-center">
|
|
||||||
<p class="mb-0">© 2025{% include 'comps/forma.html' %}. Todos los derechos reservados.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
@ -1,34 +0,0 @@
|
|||||||
<nav class="navbar navbar-expand-lg bg-body-tertiary navbar-custom">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<a class="navbar-brand" href="{{ url_for('home') }}">
|
|
||||||
{% include 'comps/forma.html' %}
|
|
||||||
</a>
|
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
|
||||||
<span class="navbar-toggler-icon"></span>
|
|
||||||
</button>
|
|
||||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
|
||||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0 effect-3">
|
|
||||||
|
|
||||||
<li class="nav-item dropdown">
|
|
||||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
|
||||||
<i class="bi bi-person-circle"></i> Usuario
|
|
||||||
</a>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
|
|
||||||
<!-- <li><a class="dropdown-item" href="#">Another action</a></li> -->
|
|
||||||
<li><hr class="dropdown-divider"></li>
|
|
||||||
<!-- <li><a class="dropdown-item" href="#">Something else here</a></li> -->
|
|
||||||
<li>
|
|
||||||
<a class="dropdown-item" href="{{ url_for('logout') }}">
|
|
||||||
<i class="bi bi-door-open-fill"></i> Logout
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<!-- Asegúrate de tener esto al final del body -->
|
|
||||||
<!-- <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> -->
|
|
@ -1,172 +0,0 @@
|
|||||||
{% extends 'template.html' %}
|
|
||||||
{% block css %}
|
|
||||||
|
|
||||||
{% endblock css %}
|
|
||||||
|
|
||||||
{% block navbar %}
|
|
||||||
{% include 'comps/navbar.html' %}
|
|
||||||
{% endblock navbar %}
|
|
||||||
|
|
||||||
{% block body %}
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.form-container {
|
|
||||||
max-width: 800px;
|
|
||||||
margin: 2rem auto;
|
|
||||||
padding: 2rem;
|
|
||||||
background: white;
|
|
||||||
border-radius: 12px;
|
|
||||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
|
|
||||||
font-family: 'Segoe UI', Roboto, sans-serif;
|
|
||||||
animation: fadeIn 0.5s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fadeIn {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(-10px);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-header {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-row {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 1.2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-label {
|
|
||||||
flex: 0 0 180px;
|
|
||||||
text-align: left;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #2d3748;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control {
|
|
||||||
flex: 1;
|
|
||||||
padding: 0.75rem;
|
|
||||||
border: 1px solid #e2e8f0;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 1rem;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: #4299e1;
|
|
||||||
box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 200px;
|
|
||||||
margin: 1.5rem auto 0;
|
|
||||||
padding: 0.75rem;
|
|
||||||
background-color: #4299e1;
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 1rem;
|
|
||||||
font-weight: 500;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background-color 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary:hover {
|
|
||||||
background-color: #3182ce;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="form-container">
|
|
||||||
<div class="form-header">
|
|
||||||
<h2>Contáctanos</h2>
|
|
||||||
<p>Déjanos tus datos y nos pondremos en contacto contigo</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Mensajes Flash -->
|
|
||||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
|
||||||
{% if messages %}
|
|
||||||
{% for category, message in messages %}
|
|
||||||
<div class="alert alert-{{ category }}">
|
|
||||||
{{ message }}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
{% endwith %}
|
|
||||||
|
|
||||||
<form method="POST" action="{{ url_for('contact') }}">
|
|
||||||
{{ form.hidden_tag() }}
|
|
||||||
{{ form.csrf_token }}
|
|
||||||
|
|
||||||
<!-- {# Nombre #} -->
|
|
||||||
<div class="form-row">
|
|
||||||
{{ form.nombre.label(class_="form-label") }}
|
|
||||||
{{ form.nombre(placeholder="Tu nombre", class_="form-control", maxlength="30") }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- {# Apellido #} -->
|
|
||||||
<div class="form-row">
|
|
||||||
{{ form.apellido.label(class_="form-label") }}
|
|
||||||
{{ form.apellido(placeholder="Tu apellido", class_="form-control", maxlength="50") }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- {# Email #} -->
|
|
||||||
<div class="form-row">
|
|
||||||
{{ form.email.label(class_="form-label") }}
|
|
||||||
{{ form.email(placeholder="ejemplo@email.com", class_="form-control", maxlength="35") }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- {# Estado #} -->
|
|
||||||
<div class="form-row">
|
|
||||||
{{ form.estado.label(class_="form-label") }}
|
|
||||||
{{ form.estado(class_="form-select", maxlength="35") }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- {# Número telefónico #} -->
|
|
||||||
<div class="form-row">
|
|
||||||
{{ form.num_tel.label(class_="form-label") }}
|
|
||||||
{{ form.num_tel(placeholder="+52 55 1234 5678", class_="form-control", maxlength="16") }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- {# Tamaño de la compañia #} -->
|
|
||||||
<div class="form-row">
|
|
||||||
{{ form.size_co.label(class_="form-label") }}
|
|
||||||
{{ form.size_co(class_="form-select", maxlength="40") }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- {# rol del contacto dentro de la empresa #} -->
|
|
||||||
<div class="form-row">
|
|
||||||
{{ form.rol_contacto.label(class_="form-label") }}
|
|
||||||
{{ form.rol_contacto(placeholder="Tu puesto o rol", class_="form-select", maxlength="50") }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- {# Sector de la empresa #} -->
|
|
||||||
<div class="form-row">
|
|
||||||
{{ form.industry_type.label(class_="form-label") }}
|
|
||||||
{{ form.industry_type(placeholder="Sector de tu empresa", class_="form-select", maxlength="40") }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-row">
|
|
||||||
{{ form.tipo_req.label(class_="form-label") }}
|
|
||||||
{{ form.tipo_req(placeholder="Describe cómo podemos ayudarte...", class_="form-control", maxlength="250") }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary" >
|
|
||||||
Enviar Mensaje
|
|
||||||
<i class="bi bi-send-check-fill"></i>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endblock body %}
|
|
74
templates/d_methodology/methodology.html
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
{% extends 'template.html' %}
|
||||||
|
{% block css %}
|
||||||
|
<link rel="stylesheet" href="{{url_for('static', filename='d_methodology/methodology.css')}}">
|
||||||
|
{% endblock css %}
|
||||||
|
|
||||||
|
{% block navbar %}
|
||||||
|
{% include 'z_comps/navbar.html' %}
|
||||||
|
{% endblock navbar %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="container py-5">
|
||||||
|
<h2 class="text-center mb-4">Nuestra Metodología</h2>
|
||||||
|
<p class="text-center text-muted mb-5">Implementamos soluciones con enfoque humano, técnico y estratégico.</p>
|
||||||
|
|
||||||
|
<div class="row g-4">
|
||||||
|
<div class="col-md-6" data-aos="fade-up" data-aos-delay="0" data-aos-duration="800">
|
||||||
|
<div class="card shadow-sm h-100" style="border: 2px solid#90b83b;">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title" style="color: #90b83b;"><i class="bi bi-bar-chart-line me-2"></i> ANÁLISIS DE DATOS</h5>
|
||||||
|
<p class="card-text">Identificamos información clave para entender las metas, retos y oportunidades estratégicas de tu empresa.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6" data-aos="fade-up" data-aos-delay="200" data-aos-duration="800">
|
||||||
|
<div class="card shadow-sm h-100" style="border: 2px solid#8c1f5a;">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title" style="color: #8c1f5a;"><i class="bi bi-sliders me-2"></i> PERSONALIZACIÓN</h5>
|
||||||
|
<p class="card-text">Diseñamos estrategias a la medida, utilizando metodologías especializadas y herramientas tecnológicas.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6" data-aos="fade-up" data-aos-delay="400" data-aos-duration="800">
|
||||||
|
<div class="card shadow-sm h-100" style="border: 2px solid#5d9dd1;">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title" style="color: #5d9dd1;"><i class="bi bi-people me-2"></i> EXPERIENCIA DE SERVICIO</h5>
|
||||||
|
<p class="card-text">Acompañamos cada paso del proyecto, asegurando una experiencia cercana, ágil y efectiva.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6" data-aos="fade-up" data-aos-delay="600" data-aos-duration="800">
|
||||||
|
<div class="card shadow-sm h-100" style="border: 2px solid#002e72;">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title" style="color: #002e72;"><i class="bi bi-graph-up-arrow me-2"></i> IMPACTO</h5>
|
||||||
|
<p class="card-text">Medimos resultados con métricas claras para asegurar que cada intervención tenga efectos tangibles.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-12" data-aos="fade-up" data-aos-delay="800" data-aos-duration="800">
|
||||||
|
<div class="card shadow-sm h-100" style="border: 2px solid#707272;">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title" style="color: #707272;"><i class="bi bi-arrow-repeat me-2"></i> SOSTENIBILIDAD DEL CAMBIO</h5>
|
||||||
|
<p class="card-text">Reforzamos capacidades internas para que el valor se mantenga a largo plazo, más allá del proyecto.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock body %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
|
||||||
|
<!-- {# aos script #} -->
|
||||||
|
{% include 'z_comps/aos_script.html' %}
|
||||||
|
|
||||||
|
{% endblock js %}
|
135
templates/e_blog/a_all_posts.html
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
{% extends 'template.html' %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='e_blog/a_all_posts.css') }}">
|
||||||
|
{% endblock css %}
|
||||||
|
|
||||||
|
{% block navbar %}
|
||||||
|
{% include 'z_comps/navbar.html' %}
|
||||||
|
{% endblock navbar %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
<form method="get" class="mb-4">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control" name="q" placeholder="Buscar título, autor o contenido..." value="{{ search }}">
|
||||||
|
<button class="btn btn-outline-primary" type="submit">
|
||||||
|
<i class="bi bi-search"></i> Buscar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<!-- {# i pagination #} -->
|
||||||
|
<nav aria-label="Page navigation">
|
||||||
|
<ul class="pagination justify-content-center mt-4">
|
||||||
|
|
||||||
|
<!-- Anterior -->
|
||||||
|
<li class="page-item {% if current_page <= 1 %}disabled{% endif %}">
|
||||||
|
<a class="page-link" href="?page={{ current_page - 1 }}{% if search %}&q={{ search }}{% endif %}">Anterior</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<!-- Páginas -->
|
||||||
|
{% for page_num in range(1, total_pages + 1) %}
|
||||||
|
<li class="page-item {% if page_num == current_page %}active{% endif %}">
|
||||||
|
<a class="page-link" href="?page={{ page_num }}{% if search %}&q={{ search }}{% endif %}">
|
||||||
|
{{ page_num }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<!-- Siguiente -->
|
||||||
|
<li class="page-item {% if current_page >= total_pages %}disabled{% endif %}">
|
||||||
|
<a class="page-link" href="?page={{ current_page + 1 }}{% if search %}&q={{ search }}{% endif %}">Siguiente</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<!-- {# f pagination #} -->
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
{% for post in data %}
|
||||||
|
<div class="col-12 col-sm-8 col-md-6 col-lg-4">
|
||||||
|
<div class="card" data-aos="fade-up" data-aos-delay="0" data-aos-duration="800">
|
||||||
|
|
||||||
|
<img class="card-img" src="{{ post[7] if post[7] else url_for('static', filename='y_img/other/no_img.png') }}" alt="">
|
||||||
|
|
||||||
|
<!-- <div class="card-img-overlay"><a href="#" class="btn btn-light btn-sm">Cooking</a></div> -->
|
||||||
|
<div class="card-body">
|
||||||
|
<h4 class="card-title">
|
||||||
|
<a href="{{ url_for('blog_post', post_id = post[0] ) }}" class="btn btn-info">
|
||||||
|
{{ post[5] }} <!-- {# título #} -->
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
<small class="text-muted cat">
|
||||||
|
<!-- {# autor #} -->
|
||||||
|
<i class="bi bi-person-circle"></i> {{ post[1] }} {{ post[2] }} <br>
|
||||||
|
<i class="bi bi-clock-history"></i> {{ post[8] }} Min.
|
||||||
|
<!-- <i class="far fa-clock text-info"></i> 30 minutes -->
|
||||||
|
<!-- <i class="fas fa-users text-info"></i> 4 portions -->
|
||||||
|
</small>
|
||||||
|
<!-- {# breve contenido #} -->
|
||||||
|
<p class="card-text">{{ post[6] }}...</p>
|
||||||
|
<!-- <a href="{{ post[0] }}" class="btn btn-info">
|
||||||
|
<i class="bi bi-eye-fill"></i>
|
||||||
|
</a> -->
|
||||||
|
</div>
|
||||||
|
<div class="card-footer text-muted d-flex justify-content-between bg-transparent border-top-0">
|
||||||
|
<div class="views">
|
||||||
|
<i class="bi bi-vector-pen"></i> {{ post[3] }}
|
||||||
|
{% if post[4] is not none %}
|
||||||
|
<i class="bi bi-arrow-repeat"></i> {{ post[4] }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<!-- {#
|
||||||
|
<div class="stats">
|
||||||
|
<i class="bi bi-eye-fill"></i> No. Vistas
|
||||||
|
<i class="far fa-comment"></i> 12
|
||||||
|
</div>
|
||||||
|
#} -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<!-- {# i pagination #} -->
|
||||||
|
<nav aria-label="Page navigation">
|
||||||
|
<ul class="pagination justify-content-center mt-4">
|
||||||
|
|
||||||
|
<!-- Anterior -->
|
||||||
|
<li class="page-item {% if current_page <= 1 %}disabled{% endif %}">
|
||||||
|
<a class="page-link" href="?page={{ current_page - 1 }}{% if search %}&q={{ search }}{% endif %}">Anterior</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<!-- Páginas -->
|
||||||
|
{% for page_num in range(1, total_pages + 1) %}
|
||||||
|
<li class="page-item {% if page_num == current_page %}active{% endif %}">
|
||||||
|
<a class="page-link" href="?page={{ page_num }}{% if search %}&q={{ search }}{% endif %}">
|
||||||
|
{{ page_num }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<!-- Siguiente -->
|
||||||
|
<li class="page-item {% if current_page >= total_pages %}disabled{% endif %}">
|
||||||
|
<a class="page-link" href="?page={{ current_page + 1 }}{% if search %}&q={{ search }}{% endif %}">Siguiente</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<!-- {# f pagination #} -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock body %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
|
||||||
|
<!-- {# aos script #} -->
|
||||||
|
{% include 'z_comps/aos_script.html' %}
|
||||||
|
|
||||||
|
{% endblock js %}
|
81
templates/e_blog/b_post.html
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
{% extends 'template.html' %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
{% endblock css %}
|
||||||
|
|
||||||
|
{% block navbar %}
|
||||||
|
{% include 'z_comps/navbar.html' %}
|
||||||
|
{% endblock navbar %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
|
||||||
|
<h1>{{data[5]}}</h1>
|
||||||
|
<h5>
|
||||||
|
<i class="bi bi-person-circle"></i> {{data[0]}} {{data[1]}} |
|
||||||
|
<i class="bi bi-pencil-square"></i> {{data[2]}} |
|
||||||
|
{% if data[3] is not none %}
|
||||||
|
<i class="bi bi-arrow-repeat"></i> {{data[3]}} |
|
||||||
|
{% endif %}
|
||||||
|
<i class="bi bi-clock-history"></i> {{ data[4] }} Minutos
|
||||||
|
</h5>
|
||||||
|
|
||||||
|
<div style="width: 60vw;">
|
||||||
|
{{data[6] | safe}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- inicio pruebas scroll to up -->
|
||||||
|
<style>
|
||||||
|
#btn-subir {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 10%;
|
||||||
|
right: 5%;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #333;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 18px;
|
||||||
|
box-shadow: 0 2px 5px rgba(0,0,0,0.3);
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#btn-subir:hover {
|
||||||
|
background: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.oculto {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<button id="btn-subir" class="oculto">↑</button>
|
||||||
|
<script>
|
||||||
|
const btnSubir = document.getElementById('btn-subir');
|
||||||
|
|
||||||
|
// Mostrar u ocultar el botón según el scroll
|
||||||
|
window.addEventListener('scroll', () => {
|
||||||
|
if (window.scrollY > 300) { // Mostrar después de 300px de scroll
|
||||||
|
btnSubir.classList.remove('oculto');
|
||||||
|
} else {
|
||||||
|
btnSubir.classList.add('oculto');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Función para subir al inicio
|
||||||
|
btnSubir.addEventListener('click', () => {
|
||||||
|
window.scrollTo({
|
||||||
|
top: 0,
|
||||||
|
behavior: 'smooth' // Desplazamiento suave
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<!-- fin pruebas -->
|
||||||
|
|
||||||
|
{% endblock body %}
|
||||||
|
{% block js %}
|
||||||
|
{% endblock js %}
|
122
templates/f_contact/contact.html
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
{% extends 'template.html' %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='f_contact/form.css') }}">
|
||||||
|
{% endblock css %}
|
||||||
|
|
||||||
|
{% block navbar %}
|
||||||
|
{% include 'z_comps/navbar.html' %}
|
||||||
|
{% endblock navbar %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
.form-header{
|
||||||
|
width: 100% !important;
|
||||||
|
& img {
|
||||||
|
width: 100px;
|
||||||
|
height: auto !important;
|
||||||
|
animation: rotacion 10s linear infinite;
|
||||||
|
transform-origin: center center; /* Asegura que gire desde el centro */
|
||||||
|
display: inline-block; /* Para mejor comportamiento en algunos navegadores */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rotacion {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="form-container" data-aos="zoom-out-down" data-aos-delay="0" data-aos-duration="800" data-aos-easing="ease-in-out">
|
||||||
|
|
||||||
|
<div class="form-header">
|
||||||
|
<h2>Contáctanos</h2>
|
||||||
|
<p>Déjanos tus datos y nos pondremos en contacto contigo.</p>
|
||||||
|
<img src="{{ url_for( 'static', filename='y_img/logos/circulo_logo.png' ) }}" alt="">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form method="POST" action="{{ url_for('contact') }}">
|
||||||
|
{{ form.hidden_tag() }}
|
||||||
|
<!-- {# {{ form.csrf_token }} #} -->
|
||||||
|
|
||||||
|
<!-- {# Nombre #} -->
|
||||||
|
<div class="form-row">
|
||||||
|
<!-- {{ form.nombre.label(class_="form-label") }} -->
|
||||||
|
{{ form.nombre(placeholder="Tu nombre", class_="form-control", maxlength="30") }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- {# Apellido #} -->
|
||||||
|
<div class="form-row">
|
||||||
|
<!-- {{ form.apellido.label(class_="form-label") }} -->
|
||||||
|
{{ form.apellido(placeholder="Tu apellido", class_="form-control", maxlength="50") }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- {# Email #} -->
|
||||||
|
<div class="form-row">
|
||||||
|
<!-- {{ form.email.label(class_="form-label") }} -->
|
||||||
|
{{ form.email(placeholder="ejemplo@email.com", class_="form-control", maxlength="35", type="email") }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- {# Estado #} -->
|
||||||
|
<div class="form-row">
|
||||||
|
<!-- {{ form.estado.label(class_="form-label") }} -->
|
||||||
|
{{ form.estado(class_="form-select", maxlength="35") }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- {# Número telefónico #} -->
|
||||||
|
<div class="form-row">
|
||||||
|
{{ form.num_tel(placeholder="55 1234 5678", class_="form-control", maxlength="16", pattern="^\d{2}\s\d{4}\s\d{4}$", title="Formato ejemplo: 55 1234 5678") }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- {# Tamaño de la compañia #} -->
|
||||||
|
<div class="form-row">
|
||||||
|
{{ form.size_co(class_="form-select", maxlength="40") }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- {# rol del contacto dentro de la empresa #} -->
|
||||||
|
<div class="form-row">
|
||||||
|
{{ form.rol_contacto(placeholder="Tu puesto o rol", class_="form-select", maxlength="50") }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- {# Sector de la empresa #} -->
|
||||||
|
<div class="form-row">
|
||||||
|
{{ form.industry_type(placeholder="Sector de tu empresa", class_="form-select", maxlength="40") }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
{{ form.tipo_req(placeholder="Describe cómo podemos ayudarte...", class_="form-control", maxlength="250", rows="6") }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
Enviar Mensaje
|
||||||
|
<i class="bi bi-send-check-fill"></i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock body %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
|
||||||
|
<script type="module" src="{{ url_for('static', filename='f_contact/re_phone.js') }}"></script>
|
||||||
|
|
||||||
|
<!-- {# if flash #} -->
|
||||||
|
{% include 'z_comps/if_flash.html' %}
|
||||||
|
|
||||||
|
<!-- {# aos script #} -->
|
||||||
|
{% include 'z_comps/aos_script.html' %}
|
||||||
|
|
||||||
|
{% endblock js %}
|
||||||
|
|
64
templates/g_login/login.html
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
{% extends 'template.html' %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='f_contact/form.css') }}">
|
||||||
|
{% endblock css %}
|
||||||
|
|
||||||
|
{% block navbar %}
|
||||||
|
{% include 'z_comps/navbar.html' %}
|
||||||
|
{% endblock navbar %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
main{
|
||||||
|
place-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="form-container" data-aos="fade-down" data-aos-delay="0" data-aos-duration="800" data-aos-easing="ease-in-out">
|
||||||
|
|
||||||
|
<div class="form-header">
|
||||||
|
<h2 class="form-title">Bienvenido de vuelta</h2>
|
||||||
|
<p class="form-subtitle">Ingresa tus credenciales para continuar</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form method="POST" action="{{ url_for('login') }}" class="login-form">
|
||||||
|
{{ form.hidden_tag() }}
|
||||||
|
|
||||||
|
<!-- {# email usuario #} -->
|
||||||
|
<div class="form-row">
|
||||||
|
{{ form.email(class_="form-control", placeholder="usuario@email.com", type="email") }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- {# password usuario #} -->
|
||||||
|
<div class="form-row">
|
||||||
|
{{ form.password(class_="form-control", placeholder="L4_c0ntr4z3ñ4", autocomplete="on") }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
Iniciar Sesión
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="form-footer">
|
||||||
|
<a href="{{ url_for('recover_pswd') }}" class="forgot-password">¿Olvidaste tu contraseña?</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock body %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
|
||||||
|
<!-- {# if flash #} -->
|
||||||
|
{% include 'z_comps/if_flash.html' %}
|
||||||
|
|
||||||
|
|
||||||
|
<!-- {# aos script #} -->
|
||||||
|
{% include 'z_comps/aos_script.html' %}
|
||||||
|
|
||||||
|
{% endblock js %}
|
64
templates/g_login/recover_pswd.html
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
{% extends 'template.html' %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='f_contact/form.css') }}">
|
||||||
|
{% endblock css %}
|
||||||
|
|
||||||
|
{% block navbar %}
|
||||||
|
{% include 'z_comps/navbar.html' %}
|
||||||
|
{% endblock navbar %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
main{
|
||||||
|
place-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="form-container" data-aos="fade-up" data-aos-delay="0" data-aos-duration="800" data-aos-easing="ease-in-out">
|
||||||
|
|
||||||
|
<div class="form-header">
|
||||||
|
<h2 class="form-title">Recuperar Contraseña</h2>
|
||||||
|
<p class="form-subtitle">Ingresa tu email de usuario para recibir una contraseña temporal.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form method="POST" class="login-form" action="{{ url_for('recover_pswd') }}" >
|
||||||
|
{{ form.hidden_tag() }}
|
||||||
|
<!-- {# {{ form.csrf_token }} #} -->
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<!-- {{ form.email.label(class="form-label") }} -->
|
||||||
|
{{ form.email(class="form-control", placeholder="usuario@email.com", type="email") }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary w-100 py-2 mb-3">
|
||||||
|
Recuperar Contraseña
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="form-footer">
|
||||||
|
<a href="{{ url_for('login') }}" class="forgot-password">
|
||||||
|
<i class="bi bi-arrow-left me-1"></i> Regresar al Log In
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock body %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
|
||||||
|
<!-- {# if flash #} -->
|
||||||
|
{% include 'z_comps/if_flash.html' %}
|
||||||
|
|
||||||
|
|
||||||
|
<!-- {# aos script #} -->
|
||||||
|
{% include 'z_comps/aos_script.html' %}
|
||||||
|
|
||||||
|
{% endblock js %}
|
285
templates/h_tmp_usr/a_home.html
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
{% extends 'h_tmp_usr/z_tmp.html' %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
|
||||||
|
<!-- {# Librería de aos.js [animaciones] #} -->
|
||||||
|
<link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<!-- {# bootstrap datatables #} -->
|
||||||
|
<link rel="stylesheet" href="https://cdn.datatables.net/2.2.2/css/dataTables.bootstrap5.css">
|
||||||
|
|
||||||
|
<!-- {# cdn download xlsx #} -->
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
|
||||||
|
|
||||||
|
{% endblock css %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<h1>Solicitudes de Contacto</h1>
|
||||||
|
|
||||||
|
{% if is_admin %}
|
||||||
|
{% include 'z_comps/download_xlsx.html' %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<table id="dbContact" class="table table-striped" style="width:100%" data-aos="fade-right" data-aos-delay="0" data-aos-duration="800" data-aos-easing="ease-in-out">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Fecha</th>
|
||||||
|
<th>Nombre Completo</th>
|
||||||
|
<th>Estado</th>
|
||||||
|
<th>Tamaño Empresa</th>
|
||||||
|
<th>Rol Contacto</th>
|
||||||
|
<th>Tipo Industria</th>
|
||||||
|
<th>Estatus</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for item in data_contact %}
|
||||||
|
<tr data-id="{{item[0]}}">
|
||||||
|
<td>
|
||||||
|
<button data-id="{{item[0]}}" type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#modalContactData">
|
||||||
|
{{item[1]}}
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{item[2]}} {{item[3]}}
|
||||||
|
</td>
|
||||||
|
<td>{{item[4]}}</td>
|
||||||
|
<td>{{item[5]}}</td>
|
||||||
|
<td>{{item[6]}}</td>
|
||||||
|
<td>{{item[7]}}</td>
|
||||||
|
<td>
|
||||||
|
<select class="form-select" data-id="{{ item[0] }}">
|
||||||
|
<option value="Null" {% if item[8] is none %}selected{% endif %}></option>
|
||||||
|
<option value="Opción 1" {% if item[8] == 'Opción 1' %}selected{% endif %}>Opción 1</option>
|
||||||
|
<option value="Opción 2" {% if item[8] == 'Opción 2' %}selected{% endif %}>Opción 2</option>
|
||||||
|
<option value="Opción 3" {% if item[8] == 'Opción 3' %}selected{% endif %}>Opción 3</option>
|
||||||
|
<option value="Opción 4" {% if item[8] == 'Opción 4' %}selected{% endif %}>Opción 4</option>
|
||||||
|
<option value="Opción 5" {% if item[8] == 'Opción 5' %}selected{% endif %}>Opción 5</option>
|
||||||
|
<option value="Opción 6" {% if item[8] == 'Opción 6' %}selected{% endif %}>Opción 6</option>
|
||||||
|
<option value="Opción 7" {% if item[8] == 'Opción 7' %}selected{% endif %}>Opción 7</option>
|
||||||
|
<option value="Archivado" >Archivar</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- Modal -->
|
||||||
|
<div class="modal fade" id="modalContactData" tabindex="-1" aria-labelledby="modalContactDataLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="modalContactDataLabel"><i class="bi bi-database-fill-check"></i> Información del Registro:</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body" id="modalBody">
|
||||||
|
...
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cerrar</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock body %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- {# if flash #} -->
|
||||||
|
{% include 'z_comps/if_flash.html' %}
|
||||||
|
|
||||||
|
<!-- {# aos script #} -->
|
||||||
|
{% include 'z_comps/aos_script.html' %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function getFechaHoraFormato() {
|
||||||
|
const now = new Date();
|
||||||
|
const pad = (n) => n.toString().padStart(2, '0');
|
||||||
|
const dia = pad(now.getDate());
|
||||||
|
const mes = pad(now.getMonth() + 1); // Enero es 0
|
||||||
|
const anio = now.getFullYear();
|
||||||
|
const hora = pad(now.getHours());
|
||||||
|
const minuto = pad(now.getMinutes());
|
||||||
|
return `${dia}.${mes}.${anio}_${hora}_${minuto}`;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
async function get_contact_data(btn) {
|
||||||
|
try {
|
||||||
|
let id = btn.dataset.id;
|
||||||
|
let modalBody = document.querySelector("#modalBody");
|
||||||
|
|
||||||
|
let response = await fetch('/user/get-contact-data', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ id }),
|
||||||
|
credentials: 'include'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
|
||||||
|
let { data } = await response.json();
|
||||||
|
|
||||||
|
let contact = {
|
||||||
|
fecha: data[0],
|
||||||
|
fecha_hora: data[1],
|
||||||
|
nombre: data[2],
|
||||||
|
apellido: data[3],
|
||||||
|
correo: data[4],
|
||||||
|
estado: data[5],
|
||||||
|
telefono: data[6],
|
||||||
|
tamano_empresa: data[7],
|
||||||
|
rol_contacto: data[8],
|
||||||
|
tipo_industria: data[9],
|
||||||
|
comentarios: data[10],
|
||||||
|
estatus: data[11] ?? "Sin Estatus"
|
||||||
|
};
|
||||||
|
|
||||||
|
modalBody.innerHTML = `
|
||||||
|
<p><b><i class="bi bi-calendar4-week"></i></b> ${contact.fecha} - ${contact.fecha_hora}</p>
|
||||||
|
<p><b><i class="bi bi-person-fill"></i></b> ${contact.nombre} ${contact.apellido}</p>
|
||||||
|
<p><b><i class="bi bi-envelope-at-fill"></i></b> ${contact.correo}</p>
|
||||||
|
<p><b><i class="bi bi-pin-map-fill"></i></b> ${contact.estado}</p>
|
||||||
|
<p><b><i class="bi bi-telephone-fill"></i></b> ${contact.telefono}</p>
|
||||||
|
<p><b><i class="bi bi-people-fill"></i></b> ${contact.tamano_empresa}</p>
|
||||||
|
<p><b><i class="bi bi-person-vcard"></i></b> ${contact.rol_contacto}</p>
|
||||||
|
<p><b><i class="bi bi-shop"></i></b> ${contact.tipo_industria}</p>
|
||||||
|
<p><b><i class="bi bi-wrench-adjustable"></i></b> ${contact.estatus}</p>
|
||||||
|
<p><b><i class="bi bi-file-earmark-font-fill"></i></b> ${contact.comentarios}</p>
|
||||||
|
`;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error al obtener datos:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('dbContact').addEventListener('click', (event) => {
|
||||||
|
const button = event.target.closest('button.btn-primary[data-id]');
|
||||||
|
if (button) {
|
||||||
|
get_contact_data(button);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
|
||||||
|
function send_data(target) {
|
||||||
|
let id = target.dataset.id;
|
||||||
|
let valor = target.value != "Null" ? target.value : null;
|
||||||
|
let data = { id, value: valor };
|
||||||
|
|
||||||
|
|
||||||
|
if (valor === 'Archivado') {
|
||||||
|
let table = $('#dbContact').DataTable();
|
||||||
|
let row = table.row(`tr[data-id="${id}"]`);
|
||||||
|
row.remove().draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch('/user/manage-record', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
credentials: 'include'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('dbContact').addEventListener('change', (event) => {
|
||||||
|
if (event.target.matches('select.form-select[data-id]')) {
|
||||||
|
send_data(event.target);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- js datatables -->
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.3.0/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<!-- jQuery (necesario para DataTables) -->
|
||||||
|
<script src="https://code.jquery.com/jquery-3.7.1.js"></script>
|
||||||
|
<!-- Popper.js (necesario para los dropdowns de Bootstrap) -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js"></script>
|
||||||
|
<!-- Bootstrap JS Bundle con Popper -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<!-- DataTables JS -->
|
||||||
|
<script src="https://cdn.datatables.net/2.2.2/js/dataTables.js"></script>
|
||||||
|
<!-- DataTables Bootstrap 5 JS -->
|
||||||
|
<script src="https://cdn.datatables.net/2.2.2/js/dataTables.bootstrap5.js"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
new DataTable('#dbContact', {
|
||||||
|
initComplete: function() {
|
||||||
|
// Agrega campos de filtro para cada columna
|
||||||
|
this.api().columns().every(function() {
|
||||||
|
let column = this;
|
||||||
|
let header = $(column.header());
|
||||||
|
let title = header.text().trim();
|
||||||
|
|
||||||
|
// Excluir la columna "Estatus" del filtro
|
||||||
|
if (title !== 'Estatus') {
|
||||||
|
// Crea input de filtro
|
||||||
|
header.append('<div class="filter"><input type="text" class="form-control" placeholder="'+title+'" /></div>');
|
||||||
|
|
||||||
|
// Aplica el filtro al escribir
|
||||||
|
$('input', header)
|
||||||
|
.on('keyup change', function() {
|
||||||
|
if (column.search() !== this.value) {
|
||||||
|
column.search(this.value).draw();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- <script>
|
||||||
|
new DataTable('#dbContact', {
|
||||||
|
initComplete: function() {
|
||||||
|
// Agrega campos de filtro para cada columna
|
||||||
|
this.api().columns().every(function() {
|
||||||
|
let column = this;
|
||||||
|
let header = $(column.header());
|
||||||
|
let title = header.text().trim();
|
||||||
|
|
||||||
|
// Crea input de filtro
|
||||||
|
// <i class="bi bi-filter"></i>
|
||||||
|
header.append('<div class="filter"><input type="text" class="form-control" placeholder="'+title+'" /></div>');
|
||||||
|
// Aplica el filtro al escribir
|
||||||
|
$('input', header)
|
||||||
|
.on('keyup change', function() {
|
||||||
|
if (column.search() !== this.value) {
|
||||||
|
column.search(this.value).draw();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script> -->
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let exp = "{{exp}}";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script src="{{ url_for('static', filename='z_comps/save_exp_timestamp.js') }}"></script>
|
||||||
|
|
||||||
|
{% endblock js %}
|
35
templates/h_tmp_usr/b_txt_editor.html
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{% extends 'h_tmp_usr/z_tmp.html' %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
|
||||||
|
<!-- {# estilos editor de texto #} -->
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='h_tmp_user/text_editor.css' ) }}">
|
||||||
|
|
||||||
|
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/summernote@0.9.0/dist/summernote-lite.min.css" rel="stylesheet">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/summernote@0.9.0/dist/summernote-lite.min.js"></script>
|
||||||
|
|
||||||
|
{% endblock css %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<h1>Crear una publicación</h1>
|
||||||
|
|
||||||
|
<form id="post-form" data-aos="fade-right" data-aos-delay="0" data-aos-duration="800">
|
||||||
|
<input type="text" name="title" class="form-control" placeholder="Título de la publicación" maxlength="100">
|
||||||
|
<div id="summernote"></div>
|
||||||
|
<button id="btn-submit" type="submit" class="btn btn-success"><i class="bi bi-file-earmark-richtext-fill"></i> Publicar Documento</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock body %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
<script type="module" src="{{ url_for('static', filename='h_tmp_user/add_pst.js' ) }}"></script>
|
||||||
|
|
||||||
|
<!-- {# aos script #} -->
|
||||||
|
{% include 'z_comps/aos_script.html' %}
|
||||||
|
|
||||||
|
|
||||||
|
<!-- {# flecha ir hasta arriba #} -->
|
||||||
|
{% include 'z_comps/arrow_to_up.html' %}
|
||||||
|
|
||||||
|
{% endblock js %}
|
232
templates/h_tmp_usr/c_my_posts.html
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
{% extends 'h_tmp_usr/z_tmp.html' %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
|
||||||
|
<!-- {# i jconfirm #} -->
|
||||||
|
<link rel="stylesheet" href="https://htmlguyllc.github.io/jConfirm/jConfirm.min.css">
|
||||||
|
<!-- {# f jconfirm #} -->
|
||||||
|
|
||||||
|
{% endblock css %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
|
||||||
|
<h2 class="display-5 mb-4">Mis Publicaciones</h2>
|
||||||
|
<!-- https://codepen.io/componentity/embed/RwajNdW?height=500&theme-id=dark&default-tab=result&user=componentity&slug-hash=RwajNdW&pen-title=Blog%20Card%20Section%20Bootstrap%20-%202nd&name=cp_embed_1 -->
|
||||||
|
<!-- https://codepen.io/anon/embed/dgmjKK?height=602&theme-id=dark&slug-hash=dgmjKK&default-tab=result&animations=run&editable=&embed-version=2&user=anon&name=cp_embed_23 -->
|
||||||
|
<!-- https://bootstrapbrain.com/component/bootstrap-5-blog-card/#code -->
|
||||||
|
<!-- <div class="col-md-12"> -->
|
||||||
|
<style>
|
||||||
|
.fade-out {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<!-- {# i pagination #} -->
|
||||||
|
{% if total_pages > 1 %}
|
||||||
|
<nav aria-label="Page navigation">
|
||||||
|
<ul class="pagination justify-content-center mt-4">
|
||||||
|
|
||||||
|
<li class="page-item {% if current_page == 1 %}disabled{% endif %}">
|
||||||
|
<a class="page-link" href="?page={{ current_page - 1 }}" aria-label="Previous">
|
||||||
|
<span aria-hidden="true">«</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
{% for p in range(1, total_pages + 1) %}
|
||||||
|
<li class="page-item {% if p == current_page %}active{% endif %}">
|
||||||
|
<a class="page-link" href="?page={{ p }}">{{ p }}</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<li class="page-item {% if current_page == total_pages %}disabled{% endif %}">
|
||||||
|
<a class="page-link" href="?page={{ current_page + 1 }}" aria-label="Next">
|
||||||
|
<span aria-hidden="true">»</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
{% endif %}
|
||||||
|
<!-- {# f pagination #} -->
|
||||||
|
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 row-cols-lg-4 g-4" id="card-container">
|
||||||
|
{% for ele in data %}
|
||||||
|
|
||||||
|
<div class="col card-wrapper" data-id="{{ele[0]}}" data-aos="zoom-out-left" data-aos-delay="{{loop.index * 100 }}" data-aos-duration="800" data-aos-easing="ease-in-out">
|
||||||
|
<div class="card h-100">
|
||||||
|
|
||||||
|
<img src="{{ (ele[5] if ele[5] is not none else url_for('static', filename='y_img/other/no_img.png'))|safe }}"
|
||||||
|
class="card-img-top" alt="card image">
|
||||||
|
|
||||||
|
<div class="card-body d-flex flex-column">
|
||||||
|
<div class="mb-3">
|
||||||
|
<h5 class="card-title">{{ ele[3] }}</h5>
|
||||||
|
<p class="card-text">{{ ele[4] }}...</p>
|
||||||
|
<small class="text-muted">
|
||||||
|
<i class="bi bi-calendar-week"></i> {{ ele[1] }}<br>
|
||||||
|
{% if ele[2] is not none %}
|
||||||
|
<i class="bi bi-arrow-repeat"></i> {{ ele[2] }}<br>
|
||||||
|
{% endif %}
|
||||||
|
<i class="bi bi-clock"></i> {{ ele[6] }} min.
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<div class="mt-auto">
|
||||||
|
<a class="btn btn-primary btn-sm" href="{{ url_for('post', post_id=ele[0]) }}"><i
|
||||||
|
class="bi bi-eye-fill"></i></a>
|
||||||
|
<a class="btn btn-secondary btn-sm" href="{{ url_for('edit_post', id_post=ele[0]) }}"><i
|
||||||
|
class="bi bi-vector-pen"></i></a>
|
||||||
|
<button type="button" class="btn btn-danger btn-sm delete-btn" data-id="{{ele[0]}}">
|
||||||
|
<i class="bi bi-trash3"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<!-- {# i pagination #} -->
|
||||||
|
{% if total_pages > 1 %}
|
||||||
|
<nav aria-label="Page navigation">
|
||||||
|
<ul class="pagination justify-content-center mt-4">
|
||||||
|
|
||||||
|
<li class="page-item {% if current_page == 1 %}disabled{% endif %}">
|
||||||
|
<a class="page-link" href="?page={{ current_page - 1 }}" aria-label="Previous">
|
||||||
|
<span aria-hidden="true">«</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
{% for p in range(1, total_pages + 1) %}
|
||||||
|
<li class="page-item {% if p == current_page %}active{% endif %}">
|
||||||
|
<a class="page-link" href="?page={{ p }}">{{ p }}</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<li class="page-item {% if current_page == total_pages %}disabled{% endif %}">
|
||||||
|
<a class="page-link" href="?page={{ current_page + 1 }}" aria-label="Next">
|
||||||
|
<span aria-hidden="true">»</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
{% endif %}
|
||||||
|
<!-- {# f pagination #} -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- <script>
|
||||||
|
document.querySelectorAll('.delete-btn').forEach(btn => {
|
||||||
|
btn.addEventListener('click', () => {
|
||||||
|
const wrapper = btn.closest('.card-wrapper');
|
||||||
|
wrapper.classList.add('fade-out');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
wrapper.remove();
|
||||||
|
}, 300);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script> -->
|
||||||
|
|
||||||
|
{% endblock body %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
|
||||||
|
|
||||||
|
<!-- {# aos script #} -->
|
||||||
|
{% include 'z_comps/aos_script.html' %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- jQuery y jConfirm -->
|
||||||
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
|
<script src="https://htmlguyllc.github.io/jConfirm/jConfirm.min.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function delete_post(e) {
|
||||||
|
let btn = $(this); // `this` es el botón que lanzó el jConfirm
|
||||||
|
let postId = btn.data('id'); // lee el data-id
|
||||||
|
let data = { 'id': postId };
|
||||||
|
|
||||||
|
fetch('/user/del-post', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
credentials: 'include'
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Error en la respuesta del servidor');
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
if (data.ok) {
|
||||||
|
// Aquí puedes agregar código para eliminar el post del DOM
|
||||||
|
|
||||||
|
console.log(data.message);
|
||||||
|
document.querySelector(`div [data-id='${data.id}']`).remove();
|
||||||
|
// Por ejemplo, si el post está en un elemento con clase 'post':
|
||||||
|
btn.closest('.post').remove();
|
||||||
|
} else {
|
||||||
|
console.error('Error:', data.message);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// https://htmlguyllc.github.io/jConfirm/
|
||||||
|
$(function(){
|
||||||
|
$('.delete-btn').jConfirm({
|
||||||
|
//false|array: if provided, this will override the default confirm/deny buttons (see below for an example)
|
||||||
|
btns: false,
|
||||||
|
//string: question displayed to the user
|
||||||
|
question: '¿Deseas continuar con la eliminación?',
|
||||||
|
//string: confirm button text
|
||||||
|
confirm_text: 'Sí',
|
||||||
|
//string: deny button text
|
||||||
|
deny_text: 'No',
|
||||||
|
//boolean: if true, when the confirm button is clicked the user will be redirected to the button's href location
|
||||||
|
// follow_href: true,
|
||||||
|
//boolean: if true and follow_href is true, the href will be opened in a new window
|
||||||
|
open_new_tab: false,
|
||||||
|
//boolean: if true, the tooltip will be hidden if you click outside of it
|
||||||
|
hide_on_click: true,
|
||||||
|
//string ('auto','top','bottom','left','right'): preferred location of the tooltip (defaults to auto if no space)
|
||||||
|
position: 'auto',
|
||||||
|
//boolean: if true, the deny button will be shown
|
||||||
|
show_deny_btn: true,
|
||||||
|
//string ('black', 'white', 'bootstrap-4', 'bootstrap-4-white')
|
||||||
|
theme: 'bootstrap-4',
|
||||||
|
//string ('tiny', 'small', 'medium', 'large')
|
||||||
|
size: 'medium',
|
||||||
|
//boolean: show the tooltip immediately on instantiation
|
||||||
|
show_now: false,
|
||||||
|
//string: class(es) to add to the tooltip
|
||||||
|
'class': ''
|
||||||
|
}).on('confirm', delete_post)
|
||||||
|
.on('deny', function(e){
|
||||||
|
var btn = $(this);
|
||||||
|
//do something on deny
|
||||||
|
}).on('jc-show', function(e, tooltip){
|
||||||
|
// console.log("el tooltip es visible");
|
||||||
|
//do something when tooltip is shown
|
||||||
|
//tooltip dom element is passed as the second parameter
|
||||||
|
}).on('jc-hide', function(e){
|
||||||
|
//do something when tooltip is hidden
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock js %}
|
81
templates/h_tmp_usr/d_read_post.html
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
{% extends 'h_tmp_usr/z_tmp.html' %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
|
||||||
|
{% endblock css %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
|
||||||
|
.pst-cont{
|
||||||
|
|
||||||
|
width: 50% !important;
|
||||||
|
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
|
||||||
|
& img {
|
||||||
|
max-width: 100%; /* La imagen no superará el ancho del contenedor */
|
||||||
|
height: auto; /* Mantiene la proporción */
|
||||||
|
display: block; /* Elimina espacios no deseados debajo de la imagen */
|
||||||
|
}
|
||||||
|
|
||||||
|
& p {
|
||||||
|
text-align: justify !important;
|
||||||
|
text-justify: inter-word !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .note-float-left, .note-float-right{
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .note-float-left {
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .note-float-right{
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="pst-cont">
|
||||||
|
<h1>{{data[2]}}</h1>
|
||||||
|
<span>
|
||||||
|
<i class="bi bi-calendar-week"></i> {{data[0]}}
|
||||||
|
{% if data[1] is not none %} | <i class="bi bi-arrow-repeat"></i> {{data[1]}}{% endif %}
|
||||||
|
| <i class="bi bi-clock-fill"></i> {{time_read}}
|
||||||
|
</span>
|
||||||
|
<div>
|
||||||
|
<a type="button" class="btn btn-info" href="{{ url_for('my_posts') }}"><i class="bi bi-arrow-left"></i> Mis Publicaciones.</a>
|
||||||
|
<a type="button" class="btn btn-secondary" href="{{ url_for('edit_post', id_post= post_id ) }}"><i class="bi bi-vector-pen"></i> Editar.</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{data[3] | safe}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock body %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
<!-- {# flecha ir hasta arriba #} -->
|
||||||
|
{% include 'z_comps/arrow_to_up.html' %}
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let cont = document.querySelector('div.pst-cont');
|
||||||
|
// let lst_img = cont.querySelectorAll('p img[style="width: 2515px;]');
|
||||||
|
let lst_img = cont.querySelectorAll('p img[style*="width: 2515px;"]');
|
||||||
|
lst_img.forEach(ele => {
|
||||||
|
console.log(ele);
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock js %}
|
44
templates/h_tmp_usr/e_edit_post.html
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
{% extends 'h_tmp_usr/z_tmp.html' %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
|
||||||
|
<!-- {# estilos editor de texto #} -->
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='h_tmp_user/text_editor.css' ) }}">
|
||||||
|
|
||||||
|
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/summernote@0.9.0/dist/summernote-lite.min.css" rel="stylesheet">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/summernote@0.9.0/dist/summernote-lite.min.js"></script>
|
||||||
|
|
||||||
|
{% endblock css %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h2 class="display-5 mb-4">Editar Publicación</h2>
|
||||||
|
|
||||||
|
<form id="post-form" data-aos="fade-right" data-aos-delay="0" data-aos-duration="800">
|
||||||
|
<input type="text" name="title" class="form-control" placeholder="Título de la publicación">
|
||||||
|
<div id="summernote"></div>
|
||||||
|
<button type="submit" class="btn btn-success" id="btn-submit"><i class="bi bi-file-earmark-richtext-fill"></i> Enviar Cambios</button>
|
||||||
|
<a class="btn btn-warning" href="{{ url_for('my_posts') }}" role="button" id="btn-cancel">Cancelar</a>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock body %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
|
||||||
|
<!-- {# flecha ir hasta arriba #} -->
|
||||||
|
{% include 'z_comps/arrow_to_up.html' %}
|
||||||
|
|
||||||
|
<!-- {# aos script #} -->
|
||||||
|
{% include 'z_comps/aos_script.html' %}
|
||||||
|
|
||||||
|
<script type="module" src="{{ url_for( 'static', filename='h_tmp_user/edit-post.js' ) }}"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let data = {{ data | tojson | safe }};
|
||||||
|
let id_post = {{ id_post }};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock js %}
|
62
templates/h_tmp_usr/f_change_pswd.html
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
{% extends 'h_tmp_usr/z_tmp.html' %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='f_contact/form.css') }}">
|
||||||
|
{% endblock css %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
main{
|
||||||
|
place-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="form-container" data-aos="fade-down" data-aos-delay="0" data-aos-duration="800" data-aos-easing="ease-in-out">
|
||||||
|
<div class="form-header">
|
||||||
|
<h2>Cambio de Contraseña</h2>
|
||||||
|
</div>
|
||||||
|
<form method="POST" action="{{ url_for('change_pswd') }}" class="login-form">
|
||||||
|
{{ form.hidden_tag() }}
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
{{ form.cur_pswd(class_="form-control", placeholder="Contraseña actual", id="cur_pswd", maxlength="60") }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
{{ form.new_pswd(class_="form-control", placeholder="Nueva Contraseña", id="new_pswd", maxlength="60") }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul style="list-style-type:none;">
|
||||||
|
<li><span id="lengt10">❌</span> Al menos 10 caracteres.</li>
|
||||||
|
<li><span id="atl1num">❌</span> Al menos un número.</li>
|
||||||
|
<li><span id="atl1mayusc">❌</span> Al menos una mayúscula.</li>
|
||||||
|
<li><span id="atl1chrspe">❌</span> Al menos un caracter especial: !"#$%&/()=?¡".</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
<i class="bi bi-lock-fill"></i> Cambiar de Contraseña
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock body %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
<script type="module" src="{{ url_for( 'static', filename='h_tmp_user/change_pswd.js' ) }}"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- {# aos script #} -->
|
||||||
|
{% include 'z_comps/aos_script.html' %}
|
||||||
|
|
||||||
|
<!-- {# if flash #} -->
|
||||||
|
{% include 'z_comps/if_flash.html' %}
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock js %}
|