add: guard in router

This commit is contained in:
Alexandro Uc Santos
2023-11-24 21:07:42 -06:00
parent afa8e1983b
commit ec02b98272
18 changed files with 914 additions and 47 deletions

View File

@@ -5,6 +5,10 @@
<link rel="icon" href="/favicon.ico"> <link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"
/>
<title>Eta viaporte</title> <title>Eta viaporte</title>
</head> </head>
<body class="bg-body"> <body class="bg-body">
@@ -12,8 +16,8 @@
<script type="module" src="/src/main.js"></script> <script type="module" src="/src/main.js"></script>
</body> </body>
<script src="https://kit.fontawesome.com/3675730ed5.js" crossorigin="anonymous"></script> <script src="https://kit.fontawesome.com/3675730ed5.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script> <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.12.9/dist/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.12.9/dist/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
</html> </html>

181
package-lock.json generated
View File

@@ -10,6 +10,7 @@
"dependencies": { "dependencies": {
"axios": "^1.6.2", "axios": "^1.6.2",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"sass": "^1.69.5",
"vue": "^3.3.4", "vue": "^3.3.4",
"vue-multiselect": "^3.0.0-beta.3", "vue-multiselect": "^3.0.0-beta.3",
"vue-router": "^4.2.5" "vue-router": "^4.2.5"
@@ -507,6 +508,18 @@
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
"integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==" "integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw=="
}, },
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/asynckit": { "node_modules/asynckit": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -522,6 +535,51 @@
"proxy-from-env": "^1.1.0" "proxy-from-env": "^1.1.0"
} }
}, },
"node_modules/binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"engines": {
"node": ">=8"
}
},
"node_modules/braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dependencies": {
"fill-range": "^7.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/chokidar": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
],
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
},
"engines": {
"node": ">= 8.10.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/combined-stream": { "node_modules/combined-stream": {
"version": "1.0.8", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -588,6 +646,17 @@
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
}, },
"node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/follow-redirects": { "node_modules/follow-redirects": {
"version": "1.15.3", "version": "1.15.3",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz",
@@ -624,7 +693,6 @@
"version": "2.3.3", "version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"optional": true, "optional": true,
"os": [ "os": [
@@ -634,6 +702,60 @@
"node": "^8.16.0 || ^10.6.0 || >=11.0.0" "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
} }
}, },
"node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dependencies": {
"is-glob": "^4.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/immutable": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz",
"integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA=="
},
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dependencies": {
"binary-extensions": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dependencies": {
"is-extglob": "^2.1.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/magic-string": { "node_modules/magic-string": {
"version": "0.30.5", "version": "0.30.5",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz",
@@ -681,11 +803,30 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
} }
}, },
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/picocolors": { "node_modules/picocolors": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
}, },
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pinia": { "node_modules/pinia": {
"version": "2.1.7", "version": "2.1.7",
"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.1.7.tgz", "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.1.7.tgz",
@@ -768,6 +909,17 @@
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
}, },
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dependencies": {
"picomatch": "^2.2.1"
},
"engines": {
"node": ">=8.10.0"
}
},
"node_modules/rollup": { "node_modules/rollup": {
"version": "3.29.4", "version": "3.29.4",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz",
@@ -784,6 +936,22 @@
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
"node_modules/sass": {
"version": "1.69.5",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.69.5.tgz",
"integrity": "sha512-qg2+UCJibLr2LCVOt3OlPhr/dqVHWOa9XtZf2OjbLs/T4VPSJ00udtgJxH3neXZm+QqX8B+3cU7RaLqp1iVfcQ==",
"dependencies": {
"chokidar": ">=3.0.0 <4.0.0",
"immutable": "^4.0.0",
"source-map-js": ">=0.6.2 <2.0.0"
},
"bin": {
"sass": "sass.js"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/source-map-js": { "node_modules/source-map-js": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
@@ -792,6 +960,17 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/vite": { "node_modules/vite": {
"version": "4.5.0", "version": "4.5.0",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz",

View File

@@ -10,6 +10,7 @@
"dependencies": { "dependencies": {
"axios": "^1.6.2", "axios": "^1.6.2",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"sass": "^1.69.5",
"vue": "^3.3.4", "vue": "^3.3.4",
"vue-multiselect": "^3.0.0-beta.3", "vue-multiselect": "^3.0.0-beta.3",
"vue-router": "^4.2.5" "vue-router": "^4.2.5"

View File

@@ -1,3 +1,7 @@
<script setup>
import Notification from './components/ui/Notification.vue';
</script>
<template> <template>
<main> <main>
<RouterView /> <RouterView />
@@ -5,12 +9,6 @@
<Notification/> <Notification/>
</template> </template>
<script setup>
import Notification from './components/ui/Notification.vue';
</script>
<style scoped> <style scoped>
</style> </style>

View File

@@ -2,9 +2,6 @@ body {
margin: 0px 0px; margin: 0px 0px;
padding: 0px; padding: 0px;
} }
.bg-body{
background-color: red;
}
.w-lg-45{ .w-lg-45{
width: 45%; width: 45%;

View File

@@ -0,0 +1,50 @@
<script setup>
import { useAuthStore } from '../../stores/auth';
const auth = useAuthStore();
</script>
<template>
<div class="noty-fixed" v-if="auth.checking">
<div class="content-noty">
<div class="body">
<img src="/images/logo.png" alt="logo" class="logo animate__animated animate__rubberBand">
<h2>Cargando...</h2>
</div>
</div>
</div>
</template>
<style scoped>
.noty-fixed {
position: fixed;
right: 0px;
top: 0px;
left: 0px;
bottom: 0px;
}
.content-noty {
width: 100vw;
height: 100vh;
padding: 16px 32px;
background-color: #FFF;
/* opacity: 0.9; */
}
.body {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
align-content: center;
margin: 0 auto;
gap: 5rem;
/* align-content: center; */
justify-content: center;
}
.logo {
width: 240px;
}
</style>

View File

@@ -28,7 +28,7 @@
.content-noty { .content-noty {
width: 100%; width: 100%;
max-width: 500px; max-width: 500px;
padding: 14px 20px; padding: 16px 32px;
background-color: white; background-color: white;
border-radius: 8px; border-radius: 8px;

View File

@@ -8,6 +8,8 @@ export const validateEmail = (email) => {
export const messagesError = (msg) => { export const messagesError = (msg) => {
switch (msg) { switch (msg) {
case 'Invalid credentials':
return 'Email y/o password incorrectos';
case 'Email is not registered!': case 'Email is not registered!':
return 'No se encontro una cuenta con este email'; return 'No se encontro una cuenta con este email';
case 'Wrong OTP': case 'Wrong OTP':

View File

@@ -1,11 +1,305 @@
<template>
<RouterView />
</template>
<script setup> <script setup>
import { RouterLink, useRoute, useRouter } from 'vue-router';
import { useAuthStore } from '../stores/auth';
import LoadingModal from '../components/ui/LoadingModal.vue';
const route = useRoute();
const router = useRouter();
const auth = useAuthStore();
$(document).ready(function() {
$('#sidebarCollapse').on('click', function () {
$('#sidebar').toggleClass('active');
});
});
const handleLogout = () => {
auth.$patch({
sesion: '',
token: '',
user: {},
});
localStorage.removeItem('session');
router.push({name: 'login'});
}
</script> </script>
<style scoped> <template>
<div class="wrapper">
<nav id="sidebar">
<div class="sidebar-header">
<div class="logo">
<!-- <img src="/images/logo.png" alt="Eta viaporte" width="120"> -->
<img src="/images/logo-eta.png" alt="Eta viaporte" width="120">
</div>
<h2 class="my-4">COVO</h2>
<p><i class="fa-solid fa-user"></i> <span class="ms-2">{{ auth.user?.first_name + ' ' + auth.user?.last_name }}</span></p>
<div class="divider"></div>
</div>
<div>
</div>
<ul class="list-unstyled components">
<li :class="[route.name === 'home' ? 'bg-nav-active' : '']">
<div>
<i class="fa-regular fa-building" :class="[route.name === 'home' ? 'router-link-active' : '']"></i>
<RouterLink
active-class="router-link-active"
class="nav-link" :to="{name: 'home'}">Empresa</RouterLink>
</div>
<!-- <i class="fa-solid fa-chevron-right"></i> -->
</li>
<li :class="[route.name === 'users' ? 'bg-nav-active' : '']">
<div>
<i class="fa-regular fa-user" :class="[route.name === 'users' ? 'router-link-active' : '']"></i>
<RouterLink
active-class="router-link-active"
class="nav-link" :to="{name: 'users'}">Usuarios</RouterLink>
</div>
<!-- <i class="fa-solid fa-chevron-right"></i> -->
</li>
<li :class="[route.name === 'calendar' ? 'bg-nav-active' : '']">
<div>
<i class="fa-regular fa-calendar" :class="[route.name === 'calendar' ? 'router-link-active' : '']"></i>
<RouterLink
active-class="router-link-active"
class="nav-link" :to="{name: 'calendar'}">Calendario</RouterLink>
</div>
<!-- <i class="fa-solid fa-chevron-right"></i> -->
</li>
<li :class="[route.name === 'loads' ? 'bg-nav-active' : '']">
<div>
<i class="fa-solid fa-box" :class="[route.name === 'loads' ? 'router-link-active' : '']"></i>
<RouterLink
active-class="router-link-active"
class="nav-link" :to="{name: 'loads'}">Cargas</RouterLink>
</div>
<!-- <i class="fa-solid fa-chevron-right"></i> -->
</li>
<li :class="[route.name === 'vehicles' ? 'bg-nav-active' : '']">
<div>
<i class="fa-solid fa-truck-fast" :class="[route.name === 'vehicles' ? 'router-link-active' : '']"></i>
<RouterLink
active-class=""
class="nav-link" :to="{name: 'vehicles'}">Vehiculos</RouterLink>
</div>
<!-- <i class="fa-solid fa-chevron-right"></i> -->
</li>
</ul>
<div class="eta-info">
<div class="divider"></div>
<a class="link-eta" href="">Aviso de privaciadad</a>
<a class="link-eta" href="">Terminos y condiciones</a>
<a class="link-eta" href="">FAQS</a>
<div class="d-flex align-items-center">
<i class="fa-solid fa-right-from-bracket"></i>
<a @click="handleLogout"
active-class=""
class="nav-link m-2">
Cerrar sesión
</a>
</div>
</div>
</nav>
<div id="content">
<nav class="navbar navbar-expand-lg navbar-light custom-navbar">
<div class="nav-items">
<button type="button" id="sidebarCollapse" class="btn btn-info btn-menu">
<i class="fas fa-align-left"></i>
</button>
<div class="nav-options">
<RouterLink
active-class="router-link-active"
class="nav-link" :to="{name: 'recovery'}">Embarques</RouterLink>
<RouterLink
active-class="router-link-active"
class="nav-link" :to="{name: 'recovery'}">Cargas</RouterLink>
</div>
</div>
</nav>
<div class="view">
<RouterView />
</div>
</div>
</div>
<LoadingModal/>
</template>
<style lang="scss" scoped>
.wrapper {
display: flex;
width: 100%;
align-items: stretch;
background-color: #f6f3f3;
}
#sidebar {
/* margin: 20px; */
display: flex;
flex-direction: column;
width: 220px;
min-height: 100vh;
/* background: #FFF;
color: #323030; */
background: #323030;
color: #FFF;
filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.10));
transition: all 0.3s;
}
#sidebar.active {
margin-left: -250px;
}
#sidebar .sidebar-header {
padding: 20px;
/* background: #FFF;
color: #323030; */
background: #323030;
color: #FFF;
margin-bottom: 8px;
}
.logo {
display: flex;
justify-content: center;
}
.sidebar-header h2 {
margin-top: 16px;
font-size: 1.4rem;
font-weight: 900;
}
#sidebar ul li {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 20px;
margin-bottom: 5px;
/* background-color: #FFF; */
background-color: #323030;
}
#sidebar ul li div{
display: flex;
align-items: center;
gap: 1rem;
}
#sidebar ul li i{
font-size: 20px;
color: #897c7c;
}
#sidebar ul li.active > a, a[aria-expanded="true"] {
color: #fff;
background: #6d7fcc;
}
a[data-toggle="collapse"] {
position: relative;
}
.custom-navbar {
display: block;
width: calc(100vw - 220px);
background-color: #FFF;
}
.btn-menu {
display: none;
}
.dropdown-toggle::after {
display: block;
position: absolute;
top: 50%;
right: 20px;
transform: translateY(-50%);
}
.view {
margin: 24px 16px;
}
.nav-items {
display: flex;
flex-direction: row;
justify-content: end;
}
.nav-options {
margin-left: 32px;
display: flex;
}
.nav-link{
cursor: pointer;
/* color: #5f5c5c; */
color: #ebd6d6;
font-size: 1.2rem;
margin-right: 1.2rem;
font-weight: 500;
}
.nav-link:hover{
/* color: #413f3c; */
color: #FFF;
}
.nav-link:focus{
/* color: #413f3c; */
color: #FFF;
}
.router-link-active{
/* color: #413f3c !important; */
color: #FFF !important;
}
.bg-nav-active {
/* background-color: #e4eff2 !important;; */
background-color: #373738 !important;;
}
.eta-info {
// position: fixed;
display: flex;
flex-direction: column;
margin-top: 20px;
bottom: 10px;
gap: 1rem;
// background-color: red;
align-items: start;
padding: 10px 16px;
}
.link-eta {
font-size: 14px;
text-decoration: none;
font-weight: 400;
color: #FFF;
opacity: 0.6;
}
@media (max-width: 768px) {
.custom-navbar {
width: 100vw;
}
.nav-items {
justify-content: space-between;
}
.btn-menu {
display: flex;
}
#sidebar {
margin-left: -220px;
}
#sidebar.active {
margin-left: 0;
}
}
</style> </style>

View File

@@ -1,6 +1,5 @@
import { createRouter, createWebHistory } from 'vue-router' import { createRouter, createWebHistory } from 'vue-router'
import AuthLayout from '../layouts/AuthLayout.vue' import AuthLayout from '../layouts/AuthLayout.vue'
import LoginView from '../views/LoginView.vue'
const router = createRouter({ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), history: createWebHistory(import.meta.env.BASE_URL),
@@ -25,21 +24,77 @@ const router = createRouter({
name: 'recovery', name: 'recovery',
component: () => import('../views/RecoveryPasswordView.vue'), component: () => import('../views/RecoveryPasswordView.vue'),
}, },
{
path: 'empresa',
name: 'company',
component: () => import('../views/CompleteRegisterView.vue')
}
] ]
}, },
{ {
path: '/dashboard', path: '/dashboard',
name: 'dashboard', name: 'dashboard',
component: () => import('../layouts/AdminLayout.vue'), component: () => import('../layouts/AdminLayout.vue'),
meta: { requiresAuth: true },
children: [ children: [
{ {
path: 'inicio', path: 'inicio',
name: 'inicio', name: 'home',
component: () => import('../views/HomeView.vue'), component: () => import('../views/HomeView.vue'),
}, },
{
path: 'usuarios',
name: 'users',
component: () => import('../views/UsersView.vue'),
},
{
path: 'calendario',
name: 'calendar',
component: () => import('../views/CalendarView.vue'),
},
{
path: 'cargas',
name: 'loads',
component: () => import('../views/LoadsView.vue'),
},
{
path: 'vehiculos',
name: 'vehicles',
component: () => import('../views/VehiclesView.vue'),
},
] ]
} }
] ]
}) });
router.beforeEach( async(to, from, next) => {
const requiresAuth = to.matched.some(url => url.meta.requiresAuth)
const session = localStorage.getItem('session');
// console.log('--------------- SE EJECUTA -----------------');
// console.log(session);
// console.log('--------------- FIN -----------------');
if(requiresAuth) {
//Comprobamos si el usuario esta authenticado
if(session) {
// try {
// const resp = await renewToken();
// if(resp.msg == 'success') {
// next();
// } else {
// next({name: 'login'})
// }
// } catch (error) {
// console.log(error);
// next({name: 'login'})
// }
next();
} else {
next({name: 'login'})
}
} else {
// No esta protegido
next();
}
});
export default router export default router

View File

@@ -1,6 +1,65 @@
import api from "../lib/axios"; import api from "../lib/axios";
import {messagesError} from '../helpers/validations'; import {messagesError} from '../helpers/validations';
export const login = async(body) => {
try {
const endpoint = "/account/authorize";
const {data} = await api.post(endpoint, body);
console.log(data);
console.log(data.accessToken);
if(data.accessToken !== null){
if(data.user.job_role !== 'driver'){
//TODO: Guardar token y datos del usuario
return {
msg: 'success',
data
};
} else {
//Remover datos de sesion
return 'Rol no autorizado';
}
} else {
return {
msg: "email y/o password incorrectos",
data: null
};
}
} catch (error) {
const errStr = error.response.data.error ?? 'Algo salio mal, intente más tarde';
return {
msg: messagesError(errStr),
data: null
};
}
}
export const renewToken = async() => {
const session = localStorage.getItem('session');
try {
const endpoint = `/account/authorize/${session}`;
const {data} = await api.get(endpoint);
console.log(data.user);
if(data.accessToken !== null){
return {
msg: 'success',
data
};
} else {
return {
msg: "Sesion expiro",
data: null
};
}
} catch (error) {
const errStr = error.response.data.error ?? 'Algo salio mal, intente más tarde';
return {
msg: 'Sesion expiro',
data: null
};
}
}
export const regiter = async(body) => { export const regiter = async(body) => {
try { try {
const endpoint = "/account/signup"; const endpoint = "/account/signup";

55
src/stores/auth.js Normal file
View File

@@ -0,0 +1,55 @@
import { defineStore } from "pinia";
import { ref, onMounted } from "vue";
import { useRouter } from 'vue-router';
import { renewToken } from '../services/auth';
import {useNotificationsStore} from './notifications';
export const useAuthStore = defineStore('auth', () => {
const router = useRouter();
const noty = useNotificationsStore();
const sesion = ref('')
const checking = ref(false);
const token = ref('')
const user = ref(null)
onMounted( async() => {
console.log('Se ejecuta onMounted auth');
checkSession();
});
const checkSession = async() => {
checking.value = true;
const resp = await renewToken();
if(resp.msg === 'success') {
localStorage.setItem('session', resp.data.session_token);
sesion.value = resp.data.session_token;
token.value = resp.data.accessToken;
user.value = resp.data.user;
checking.value = false;
} else {
noty.show = true;
noty.text = 'Sesión ha expirado, ingresa nuevamente';
noty.error = true;
checking.value = false;
router.push({name: 'login'});
}
}
const logout = () => {
localStorage.removeItem('session');
router.push({name: 'login'});
sesion.value = '';
token.value = '';
user.value = null;
}
return {
sesion,
logout,
token,
user,
checking
}
});

View File

@@ -0,0 +1,13 @@
<script setup>
</script>
<template>
<div>
<h2 class="title">Calendario</h2>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,99 @@
<script setup>
</script>
<template>
<h2 class="title">Complete su registro</h2>
<div class="card-info flex-column">
<p>¿Como te quieres registrar?</p>
<label class="container">Embarcador
<input type="radio" checked="checked" name="radio">
<span class="checkmark"></span>
</label>
<label class="container">Transportista
<input type="radio" name="radio">
<span class="checkmark"></span>
</label>
<label class="container">Broker (Embarcador)
<input type="radio" name="radio">
<span class="checkmark"></span>
</label>
<label class="container">Broker (Transportista)
<input type="radio" name="radio">
<span class="checkmark"></span>
</label>
</div>
</template>
<style lang="scss" scoped>
.container {
display: block;
position: relative;
padding-left: 35px;
margin-bottom: 12px;
cursor: pointer;
font-size: 22px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* Hide the browser's default radio button */
.container input {
position: absolute;
opacity: 0;
cursor: pointer;
}
/* Create a custom radio button */
.checkmark {
position: absolute;
top: 0;
left: 0;
height: 25px;
width: 25px;
background-color: #eee;
border-radius: 50%;
}
/* On mouse-over, add a grey background color */
.container:hover input ~ .checkmark {
background-color: #ccc;
}
/* When the radio button is checked, add a blue background */
.container input:checked ~ .checkmark {
background-color: #2196F3;
}
/* Create the indicator (the dot/circle - hidden when not checked) */
.checkmark:after {
content: "";
position: absolute;
display: none;
}
/* Show the indicator (dot/circle) when checked */
.container input:checked ~ .checkmark:after {
display: block;
}
/* Style the indicator (dot/circle) */
.container .checkmark:after {
top: 9px;
left: 9px;
width: 8px;
height: 8px;
border-radius: 50%;
background: white;
}
</style>
<!-- ¿Como te quieres registrar? (Opciones a elegir)
1 - Embarcador
2 - Transportista
3 - Broker (Embarcador)
4 - Broker (Transportista) -->

13
src/views/LoadsView.vue Normal file
View File

@@ -0,0 +1,13 @@
<script setup>
</script>
<template>
<div>
<h2 class="title">Cargas</h2>
</div>
</template>
<style scoped>
</style>

View File

@@ -4,14 +4,19 @@
import NotificationBadge from '../components/ui/NotificationBadge.vue'; import NotificationBadge from '../components/ui/NotificationBadge.vue';
import {validateEmail} from '../helpers/validations'; import {validateEmail} from '../helpers/validations';
import Spiner from '../components/ui/Spiner.vue'; import Spiner from '../components/ui/Spiner.vue';
import { RouterLink } from 'vue-router'; import { login } from '../services/auth';
import { RouterLink, useRouter } from 'vue-router';
import { useAuthStore } from '../stores/auth';
const form = reactive({ const form = reactive({
email: '', email: 'alexandro.uc.santos@gmail.com',
password: '', password: 'Password0',
}); });
const router = useRouter();
const auth = useAuthStore();
const loading = ref(false); const loading = ref(false);
const msgError = ref(''); const msgError = ref('');
const msgSuccess = ref(''); const msgSuccess = ref('');
@@ -31,7 +36,24 @@ import { RouterLink } from 'vue-router';
email: form.email, email: form.email,
password: form.password password: form.password
} }
console.log(data); const resp = await login(data);
if(resp.msg === 'success') {
console.log(resp.data.user);
if(resp.data.user.first_name && resp.data.user.last_name) {
localStorage.setItem('session', resp.data.session_token);
router.push({name: 'home'});
auth.$patch({
sesion: resp.data.session_token,
token: resp.data.accessToken,
user: resp.data.user,
})
} else {
router.push({name: 'company'});
}
} else {
msgError.value = resp.msg;
}
clearMessages();
loading.value = false; loading.value = false;
} }
} }
@@ -59,29 +81,29 @@ import { RouterLink } from 'vue-router';
<div class="d-flex flex-column my-5 justify-content-center align-items-center"> <div class="d-flex flex-column my-5 justify-content-center align-items-center">
<h2 class="title">Iniciar sesión</h2> <h2 class="title">Iniciar sesión</h2>
<p class="subtitle mt-4 mb-5">Bienvenido de vuelta! Ingresa tu email y contraseña</p> <p class="subtitle mt-4 mb-5">Bienvenido de vuelta! Ingresa tu email y contraseña</p>
<form @submit.prevent="handleLogin" class="form-content"> <form @submit.prevent="handleLogin" class="form-content">
<NotificationBadge :msg="msgError" v-if="msgError != ''"/> <NotificationBadge :msg="msgError" v-if="msgError != ''"/>
<NotificationBadge :msg="msgSuccess" :is-error="false" v-if="msgSuccess != ''"/> <NotificationBadge :msg="msgSuccess" :is-error="false" v-if="msgSuccess != ''"/>
<CustomInput <CustomInput
label="Ingresa tu correo electrónico" label="Ingresa tu correo electrónico"
name="email" name="email"
type="email" type="email"
v-model:field="form.email" v-model:field="form.email"
/> />
<CustomInput <CustomInput
label="Contraseña" label="Contraseña"
name="password" name="password"
type="password" type="password"
v-model:field="form.password" v-model:field="form.password"
/> />
<Spiner v-if="loading" class="mt-5"/> <Spiner v-if="loading" class="mt-5"/>
<input <input
v-else v-else
type="submit" type="submit"
class="btn-primary-lg btn-lg-block radius-1 mt-5" value="Ingresar"> class="btn-primary-lg btn-lg-block radius-1 mt-5" value="Ingresar">
</form> </form>
<p class="mt-3 fs-6">¿Olvidaste tu contreseña? <RouterLink class="btn-text" :to="{name: 'recovery'}">Ingresa aqui</RouterLink></p> <p class="mt-3 fs-6">¿Olvidaste tu contreseña? <RouterLink class="btn-text" :to="{name: 'recovery'}">Ingresa aqui</RouterLink></p>
<p class="mt-5 fs-6">¿No tienes una cuenta? <RouterLink class="btn-text" :to="{name: 'register'}">Registrate aqui</RouterLink></p> <p class="mt-5 fs-6">¿No tienes una cuenta? <RouterLink class="btn-text" :to="{name: 'register'}">Registrate aqui</RouterLink></p>
</div> </div>
</template> </template>

13
src/views/UsersView.vue Normal file
View File

@@ -0,0 +1,13 @@
<script setup>
</script>
<template>
<div>
<h2 class="title">Usuarios</h2>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,13 @@
<script setup>
</script>
<template>
<div>
<h2 class="title">Vehiculos</h2>
</div>
</template>
<style scoped>
</style>