Intro & Fetch
This commit is contained in:
25
package-lock.json
generated
25
package-lock.json
generated
@@ -8,6 +8,7 @@
|
|||||||
"name": "digitalpower-stock",
|
"name": "digitalpower-stock",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"axios": "^1.7.7",
|
||||||
"bootstrap": "^5.3.3",
|
"bootstrap": "^5.3.3",
|
||||||
"boxicons": "^2.1.4",
|
"boxicons": "^2.1.4",
|
||||||
"pinia": "^2.2.2",
|
"pinia": "^2.2.2",
|
||||||
@@ -2683,7 +2684,6 @@
|
|||||||
"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",
|
||||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/at-least-node": {
|
"node_modules/at-least-node": {
|
||||||
@@ -2734,6 +2734,17 @@
|
|||||||
"postcss": "^8.1.0"
|
"postcss": "^8.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/axios": {
|
||||||
|
"version": "1.7.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
|
||||||
|
"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"follow-redirects": "^1.15.6",
|
||||||
|
"form-data": "^4.0.0",
|
||||||
|
"proxy-from-env": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
@@ -3730,7 +3741,6 @@
|
|||||||
"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",
|
||||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"delayed-stream": "~1.0.0"
|
"delayed-stream": "~1.0.0"
|
||||||
@@ -4696,7 +4706,6 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.4.0"
|
"node": ">=0.4.0"
|
||||||
@@ -5902,7 +5911,6 @@
|
|||||||
"version": "1.15.9",
|
"version": "1.15.9",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||||
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
|
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "individual",
|
"type": "individual",
|
||||||
@@ -6017,7 +6025,6 @@
|
|||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"asynckit": "^0.4.0",
|
"asynckit": "^0.4.0",
|
||||||
@@ -8057,7 +8064,6 @@
|
|||||||
"version": "1.52.0",
|
"version": "1.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
@@ -8067,7 +8073,6 @@
|
|||||||
"version": "2.1.35",
|
"version": "2.1.35",
|
||||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mime-db": "1.52.0"
|
"mime-db": "1.52.0"
|
||||||
@@ -10047,6 +10052,12 @@
|
|||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/proxy-from-env": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/pseudomap": {
|
"node_modules/pseudomap": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"axios": "^1.7.7",
|
||||||
"bootstrap": "^5.3.3",
|
"bootstrap": "^5.3.3",
|
||||||
"boxicons": "^2.1.4",
|
"boxicons": "^2.1.4",
|
||||||
"pinia": "^2.2.2",
|
"pinia": "^2.2.2",
|
||||||
|
|||||||
BIN
public/DPIntro.mp4
Normal file
BIN
public/DPIntro.mp4
Normal file
Binary file not shown.
BIN
public/DigitalPower.png
Normal file
BIN
public/DigitalPower.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 237 KiB |
BIN
public/login.mp4
Normal file
BIN
public/login.mp4
Normal file
Binary file not shown.
BIN
public/notification.mp3
Normal file
BIN
public/notification.mp3
Normal file
Binary file not shown.
19
src/App.vue
19
src/App.vue
@@ -4,6 +4,9 @@
|
|||||||
</head>
|
</head>
|
||||||
<div class="window-container">
|
<div class="window-container">
|
||||||
<div id="title-bar" noselect></div>
|
<div id="title-bar" noselect></div>
|
||||||
|
<div id="notification-container" noselect></div>
|
||||||
|
<Intro v-if="ui.intro"/>
|
||||||
|
|
||||||
<Nav/>
|
<Nav/>
|
||||||
<router-view/>
|
<router-view/>
|
||||||
</div>
|
</div>
|
||||||
@@ -13,7 +16,10 @@
|
|||||||
import {onMounted, ref} from 'vue';
|
import {onMounted, ref} from 'vue';
|
||||||
import {useRouter} from 'vue-router';
|
import {useRouter} from 'vue-router';
|
||||||
import Nav from "@/components/app/Nav.vue";
|
import Nav from "@/components/app/Nav.vue";
|
||||||
|
import Intro from "@/components/app/Intro.vue";
|
||||||
|
import {useUi} from "@/pinia/ui";
|
||||||
|
|
||||||
|
const ui = useUi()
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -61,6 +67,10 @@ body {
|
|||||||
height: 720px;
|
height: 720px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
text-align: left !important;
|
||||||
|
}
|
||||||
|
|
||||||
.window-container {
|
.window-container {
|
||||||
box-shadow: 0px 0px 20px 0px #0a0a0a;
|
box-shadow: 0px 0px 20px 0px #0a0a0a;
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
@@ -68,6 +78,8 @@ body {
|
|||||||
height: 720px;
|
height: 720px;
|
||||||
background: white;
|
background: white;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
#title-bar {
|
#title-bar {
|
||||||
@@ -104,6 +116,13 @@ body {
|
|||||||
animation: slideOut var(--duration) forwards;
|
animation: slideOut var(--duration) forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#notification-container {
|
||||||
|
top: unset !important;
|
||||||
|
bottom: 2em !important;
|
||||||
|
margin-top: 6em;
|
||||||
|
right: 4% !important;
|
||||||
|
}
|
||||||
|
|
||||||
[selectedNav="true"] {
|
[selectedNav="true"] {
|
||||||
background: white;
|
background: white;
|
||||||
|
|
||||||
|
|||||||
105
src/components/app/Intro.vue
Normal file
105
src/components/app/Intro.vue
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
<script setup>
|
||||||
|
import {onMounted} from "vue";
|
||||||
|
import {useUi} from "@/pinia/ui";
|
||||||
|
|
||||||
|
const ui = useUi()
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const audio = new Audio('/DPIntro.mp4');
|
||||||
|
audio.play()
|
||||||
|
|
||||||
|
let $image = document.querySelector("#intro-image")
|
||||||
|
let $text = document.querySelector("#intro-text")
|
||||||
|
let $back = document.querySelector("#intro")
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
$image.setAttribute("class", 'active');
|
||||||
|
}, 3500)
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
$text.setAttribute("class", 'active');
|
||||||
|
}, 4500)
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
$text.setAttribute("class", 'inactive');
|
||||||
|
$image.setAttribute("class", 'inactive');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
//ui.intro = false;
|
||||||
|
$back.setAttribute("class", 'inactive');
|
||||||
|
setTimeout(() => {
|
||||||
|
ui.intro = false;
|
||||||
|
}, 3000)
|
||||||
|
}, 3000)
|
||||||
|
}, 9000)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div id="intro" flex flex-center>
|
||||||
|
<div flex flex-column flex-center>
|
||||||
|
<img id="intro-image" src="/DigitalPower.png">
|
||||||
|
<h2 id="intro-text" noselect="" class="mt-4">Hacemos tus sueños <br> realidad</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
#intro {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
background: white;
|
||||||
|
z-index: 100000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active {
|
||||||
|
animation: activate 3s forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inactive {
|
||||||
|
animation: deactivate 3s forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 240px;
|
||||||
|
pointer-events: none;
|
||||||
|
user-select: none;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
color: var(--primary);
|
||||||
|
margin: unset !important;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
img, h2 {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes activate {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes deactivate {
|
||||||
|
0% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -1,10 +1,21 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
import {useUserStore} from "@/pinia/user";
|
||||||
|
import {onBeforeMount} from "vue";
|
||||||
|
import {useRoute} from "vue-router";
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const route = useRoute();
|
||||||
|
onBeforeMount(async () => {
|
||||||
|
const user = await userStore.getUser();
|
||||||
|
if (!user && !["/#/login", "/#/register"].includes(route.fullPath)) {
|
||||||
|
location.href = ('/#/login');
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div id="app-layout">
|
<div id="app-layout">
|
||||||
<slot></slot>
|
<slot v-if="userStore.user"></slot>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,27 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import NavMenu from "@/components/app/NavMenu.vue";
|
import NavMenu from "@/components/app/NavMenu.vue";
|
||||||
import {ref} from "vue";
|
import {onMounted, ref} from "vue";
|
||||||
import {useUi} from "@/pinia/ui";
|
import {useUi} from "@/pinia/ui";
|
||||||
import {useRouter} from "vue-router";
|
import {useRouter} from "vue-router";
|
||||||
|
import {useUserStore} from "@/pinia/user";
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const ui = useUi();
|
const ui = useUi();
|
||||||
|
const userStore = useUserStore();
|
||||||
const options = ref([
|
const options = ref([
|
||||||
{
|
{
|
||||||
name: "Aplicacion", items: [
|
name: "Aplicacion", items: [
|
||||||
{name: "Manual"},
|
{name: "Manual"},
|
||||||
{name: "Solicitar Ayuda"},
|
{name: "Solicitar Ayuda"},
|
||||||
{name: "Digital Power"},
|
{name: "Digital Power"},
|
||||||
|
{
|
||||||
|
name: "Cerrar sesion",
|
||||||
|
callback: () => {
|
||||||
|
localStorage.clear();
|
||||||
|
ui.reset();
|
||||||
|
router.push("/login");
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Cerrar",
|
name: "Cerrar",
|
||||||
callback: () => {
|
callback: () => {
|
||||||
@@ -40,6 +50,7 @@ const options = ref([
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
||||||
function openMenu(items, name, url) {
|
function openMenu(items, name, url) {
|
||||||
ui.setCurrentMenu(null);
|
ui.setCurrentMenu(null);
|
||||||
if (!items) return;
|
if (!items) return;
|
||||||
@@ -68,10 +79,14 @@ function openMenu(items, name, url) {
|
|||||||
function handleClick(item) {
|
function handleClick(item) {
|
||||||
item?.callback();
|
item?.callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<nav>
|
<nav v-if="userStore.user">
|
||||||
<div class="list" noselect>
|
<div class="list" noselect>
|
||||||
<div class="item" :selectedNav="opt.name === ui.selectedMenu" :id="opt?.name" v-for="(opt, key) in options" :key
|
<div class="item" :selectedNav="opt.name === ui.selectedMenu" :id="opt?.name" v-for="(opt, key) in options" :key
|
||||||
@click="openMenu(opt?.items, opt?.name, opt?.url)"><p>
|
@click="openMenu(opt?.items, opt?.name, opt?.url)"><p>
|
||||||
|
|||||||
@@ -1,21 +1,26 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {useUi} from "@/pinia/ui";
|
import {useUi} from "@/pinia/ui";
|
||||||
|
import {onMounted} from "vue";
|
||||||
|
import {useProductStore} from "@/pinia/products";
|
||||||
|
|
||||||
const props = defineProps(["items", "headers"])
|
const props = defineProps(["items", "headers"])
|
||||||
|
const productStore = useProductStore();
|
||||||
|
onMounted(async () => {
|
||||||
|
await productStore.fetchProducts();
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div id="table-container" radius-border>
|
<div id="table-container" radius-border fade>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th v-for="(head, key) in headers" :key>{{ head?.name }}</th>
|
<th noselect v-for="(head, key) in headers" :key>{{ head?.name }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="(item, key) in items">
|
<tr v-for="(item, key) in productStore.productos">
|
||||||
<td v-for="(head, key) in headers" :class="head">{{ item?.[head?.name] }}</td>
|
<td noselect v-for="(head, key) in headers" :class="head">{{ item?.[head?.name?.toLowerCase()] }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
import {defineStore} from "pinia";
|
import {defineStore} from "pinia";
|
||||||
|
import {DBService, post} from "@/services/apiReq";
|
||||||
|
|
||||||
export const productos = defineStore('products', {
|
export const useProductStore = defineStore('products', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
productos: [],
|
productos: [],
|
||||||
|
filter: {}
|
||||||
}),
|
}),
|
||||||
getters: {
|
|
||||||
getProducts() {
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
actions: {
|
actions: {
|
||||||
fetchProducts() {
|
async fetchProducts() {
|
||||||
|
let response = await post("/get", {
|
||||||
|
limit: this.filter.limit,
|
||||||
|
page: this.filter.page,
|
||||||
|
table: 'Productos',
|
||||||
|
}, DBService)
|
||||||
|
|
||||||
|
this.productos = response.data?.rows;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -4,6 +4,8 @@ export const useUi = defineStore('ui', {
|
|||||||
state: () => ({
|
state: () => ({
|
||||||
currentMenu: null,
|
currentMenu: null,
|
||||||
selectedMenu: null,
|
selectedMenu: null,
|
||||||
|
intro: true,
|
||||||
|
login: true,
|
||||||
}),
|
}),
|
||||||
getters: {},
|
getters: {},
|
||||||
actions: {
|
actions: {
|
||||||
@@ -11,5 +13,9 @@ export const useUi = defineStore('ui', {
|
|||||||
if (!state) this.selectedMenu = null;
|
if (!state) this.selectedMenu = null;
|
||||||
this.currentMenu = state;
|
this.currentMenu = state;
|
||||||
},
|
},
|
||||||
|
reset() {
|
||||||
|
this.currentMenu = null;
|
||||||
|
this.selectedMenu = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
60
src/pinia/user.js
Normal file
60
src/pinia/user.js
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import {defineStore} from "pinia";
|
||||||
|
import {authUrl, post, site_name} from "../services/apiReq.js";
|
||||||
|
import axios from "axios";
|
||||||
|
import {clear, getObject, saveObject} from "@/services/storage";
|
||||||
|
import {show} from "@/services/notification";
|
||||||
|
|
||||||
|
export const useUserStore = defineStore('user', {
|
||||||
|
state: () => ({
|
||||||
|
user: null,
|
||||||
|
}),
|
||||||
|
getters: {},
|
||||||
|
actions: {
|
||||||
|
async login(email, password) {
|
||||||
|
clear();
|
||||||
|
let response = await axios.post(authUrl + '/login', {email, password, site_name, panel: 0});
|
||||||
|
const {User, message} = response.data;
|
||||||
|
let user = User;
|
||||||
|
if (message) {
|
||||||
|
show(response.data.message);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
if (user.role !== 'super-admin' && user.role !== 'mod-admin' && user.role !== 'admin') {
|
||||||
|
show("Usuario no encontrado");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
this.user = user;
|
||||||
|
saveObject("user", user);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
logout() {
|
||||||
|
clear();
|
||||||
|
location.reload();
|
||||||
|
},
|
||||||
|
async get() {
|
||||||
|
// || Api request
|
||||||
|
let _user = getObject("user");
|
||||||
|
if (!_user) return;
|
||||||
|
else {
|
||||||
|
let response = await axios.post(authUrl + '/user', _user);
|
||||||
|
_user = response.data?.user;
|
||||||
|
if (_user) {
|
||||||
|
this.user = _user;
|
||||||
|
saveObject("user", _user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async getUser() {
|
||||||
|
if (!this.user) {
|
||||||
|
await this.get();
|
||||||
|
}
|
||||||
|
return this.user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,27 +1,22 @@
|
|||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import {createRouter, createWebHashHistory} from 'vue-router'
|
||||||
import HomeView from '../views/HomeView.vue'
|
import HomeView from '../views/HomeView.vue'
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
name: 'home',
|
name: 'home',
|
||||||
component: HomeView
|
component: HomeView
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/about',
|
path: '/login',
|
||||||
name: 'about',
|
name: 'login',
|
||||||
// route level code-splitting
|
component: () => import('../views/Login.vue')
|
||||||
// this generates a separate chunk (about.[hash].js) for this route
|
|
||||||
// which is lazy-loaded when the route is visited.
|
|
||||||
component: function () {
|
|
||||||
return import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
]
|
]
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(process.env.BASE_URL),
|
history: createWebHashHistory(process.env.BASE_URL),
|
||||||
routes
|
routes
|
||||||
})
|
})
|
||||||
|
|
||||||
export default router
|
export default router
|
||||||
|
|||||||
135
src/services/apiReq.js
Normal file
135
src/services/apiReq.js
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import {show} from "./notification";
|
||||||
|
|
||||||
|
export const site_name = "DPStock.db";
|
||||||
|
export const backendUrl = "https://backend.digitalpower.ar";
|
||||||
|
|
||||||
|
//export const databaseUrl = "https://database.digitalpower.ar";
|
||||||
|
//export const authUrl = "https://auth.digitalpower.ar";
|
||||||
|
export const authUrl = "http://localhost:3013";
|
||||||
|
export const databaseUrl = "http://localhost:3014";
|
||||||
|
|
||||||
|
const Service = axios.create({
|
||||||
|
baseURL: `${backendUrl}/api`,
|
||||||
|
});
|
||||||
|
export const DBService = axios.create({
|
||||||
|
baseURL: `${databaseUrl}/api`,
|
||||||
|
});
|
||||||
|
export const AuthService = axios.create({
|
||||||
|
baseURL: `${authUrl}/`,
|
||||||
|
});
|
||||||
|
|
||||||
|
let user = {token: null, id: null, site_name: null};
|
||||||
|
if (localStorage.getItem("user") !== "undefined") user = JSON.parse(localStorage?.getItem("user"))
|
||||||
|
if (localStorage.getItem("user") !== "undefined")
|
||||||
|
user = JSON.parse(localStorage.getItem("user"));
|
||||||
|
|
||||||
|
|
||||||
|
let config = {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${user?.token}`,
|
||||||
|
Accept: "application/json",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export const post = (path, body, service = Service) => {
|
||||||
|
if (!user) {
|
||||||
|
user = JSON.parse(localStorage.getItem("user"));
|
||||||
|
config = {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${user?.token}`,
|
||||||
|
Accept: "application/json",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
console.log("USER", user);
|
||||||
|
}
|
||||||
|
if (!body) {
|
||||||
|
body = {};
|
||||||
|
}
|
||||||
|
if (!body?.site_name) body.site_name = user?.site_name ?? site_name;
|
||||||
|
if (user?.id) body.user_id = user?.id;
|
||||||
|
|
||||||
|
return new Promise(async (resolve) => {
|
||||||
|
service
|
||||||
|
.post(path, body, config)
|
||||||
|
.then((response) => {
|
||||||
|
let message = response?.message ?? response.data?.message;
|
||||||
|
let status = response?.status ?? response.data?.status;
|
||||||
|
|
||||||
|
if (response.data?.data) response = response?.data;
|
||||||
|
if (message) {
|
||||||
|
show(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (response?.data?.redirect) {
|
||||||
|
location.href = response?.data?.redirect;
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
if (status === 401) {
|
||||||
|
location.href = "/admin";
|
||||||
|
}
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
if (err?.response?.status === 401) {
|
||||||
|
//localStorage.removeItem("dp_user");
|
||||||
|
//location.href = "/#/login";
|
||||||
|
}
|
||||||
|
if (err?.response?.data?.message) {
|
||||||
|
show(err?.response?.data?.message);
|
||||||
|
} else
|
||||||
|
show("Ha ocurrido un error, intente mas tarde");
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const files = (path, body) => {
|
||||||
|
if (body?.append) {
|
||||||
|
body.append("site_name", user?.site_name);
|
||||||
|
if (user?.id) body.append("user_id", user?.id);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
body.site_name = user?.site_name;
|
||||||
|
if (user?.id) body.user_id = user?.id;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
Service
|
||||||
|
.post(path, body, {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "multipart/form-data",
|
||||||
|
Authorization: `Bearer ${user?.token}`,
|
||||||
|
Accept: "application/json",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.data?.message) {
|
||||||
|
show(response.data.message);
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
if (response?.data?.data?.redirect)
|
||||||
|
location.href = response?.data?.data?.redirect;
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
if (response.data?.status === 401) {
|
||||||
|
location.href = "/admin";
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(response.data);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
if (err?.response?.status === 401) {
|
||||||
|
location.href = "/#/login";
|
||||||
|
}
|
||||||
|
show("Ha ocurrido un error, intente mas tarde");
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
63
src/services/notification.js
Normal file
63
src/services/notification.js
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
export const show = (text, audio) => {
|
||||||
|
let html = `
|
||||||
|
${text}
|
||||||
|
`;
|
||||||
|
let element = document.createElement("div");
|
||||||
|
element.innerHTML = html;
|
||||||
|
element.classList.add("notification");
|
||||||
|
element.classList.add("notification-enter");
|
||||||
|
|
||||||
|
const container = document.getElementById("notification-container");
|
||||||
|
container?.appendChild(element);
|
||||||
|
PlayNotificationAudio(audio)
|
||||||
|
setTimeout(() => {
|
||||||
|
element.classList.remove("notification-enter")
|
||||||
|
element.classList.add("notification-exit");
|
||||||
|
}, 3000);
|
||||||
|
setTimeout(() => {
|
||||||
|
element.remove();
|
||||||
|
}, 3240);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ConfirmModal = () => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
let container = document.getElementById("app-layout");
|
||||||
|
let html = document.createElement("div");
|
||||||
|
html.setAttribute("id", "confirmModal");
|
||||||
|
html.innerHTML = `
|
||||||
|
<div class="overlay" overlay></div>
|
||||||
|
<div class="custom-modal-html alert-modal" shadow fade radius-border>
|
||||||
|
<div class="title alert-title">
|
||||||
|
<h1>Alerta</h1>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<p>¿Esta seguro que desea continuar con esta accion? Es posible que no tenga retroceso</p>
|
||||||
|
<br>
|
||||||
|
<div class="actions" flex-center>
|
||||||
|
<a class="custom-button-html red" id="confirmModalButton">Continuar</a>
|
||||||
|
<a class="custom-button-html" id="cancelModalButton">Cancelar</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
container?.appendChild(html);
|
||||||
|
|
||||||
|
const confirm = document.getElementById("confirmModalButton");
|
||||||
|
const cancel = document.getElementById("cancelModalButton")
|
||||||
|
|
||||||
|
confirm?.addEventListener("click", (e) => {
|
||||||
|
html.remove();
|
||||||
|
resolve(true);
|
||||||
|
})
|
||||||
|
cancel?.addEventListener("click", (e) => {
|
||||||
|
html.remove();
|
||||||
|
resolve(false);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function PlayNotificationAudio(path = '/notification.mp3') {
|
||||||
|
const audio = new Audio(path);
|
||||||
|
audio.play()
|
||||||
|
|
||||||
|
}
|
||||||
13
src/services/storage.js
Normal file
13
src/services/storage.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
export function clear() {
|
||||||
|
localStorage.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function saveObject(key, object) {
|
||||||
|
localStorage.setItem(key, JSON.stringify(object));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getObject(key) {
|
||||||
|
const item = localStorage.getItem(key);
|
||||||
|
if (item) return JSON.parse(item);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -382,7 +382,7 @@ p {
|
|||||||
|
|
||||||
[fade] {
|
[fade] {
|
||||||
animation-name: fadeAnim;
|
animation-name: fadeAnim;
|
||||||
animation-duration: 0.2s;
|
animation-duration: .5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
[delayedfade] {
|
[delayedfade] {
|
||||||
|
|||||||
@@ -11,11 +11,20 @@ import Table from "@/components/productos/Table.vue";
|
|||||||
import {computed, onMounted, ref} from "vue";
|
import {computed, onMounted, ref} from "vue";
|
||||||
import Filters from "@/components/productos/Filters.vue";
|
import Filters from "@/components/productos/Filters.vue";
|
||||||
import {useUi} from "@/pinia/ui";
|
import {useUi} from "@/pinia/ui";
|
||||||
|
import {show} from "@/services/notification";
|
||||||
|
import {useUserStore} from "@/pinia/user";
|
||||||
|
|
||||||
const ui = useUi();
|
const ui = useUi();
|
||||||
|
const userStore = useUserStore()
|
||||||
const headers = computed(() => {
|
const headers = computed(() => {
|
||||||
const producto = productos.value?.[0];
|
const producto = productos.value?.[0];
|
||||||
if (!producto) return [];
|
if (!producto) return [
|
||||||
|
{value: 'Nombre', name: 'Nombre'},
|
||||||
|
{value: 'Precio', name: 'Precio'},
|
||||||
|
{value: 'Stock', name: 'Stock'},
|
||||||
|
{value: 'Vendidos', name: 'Vendidos'},
|
||||||
|
{value: 'Acciones', name: 'Acciones'},
|
||||||
|
];
|
||||||
|
|
||||||
return Object.keys(producto).map((x) => {
|
return Object.keys(producto).map((x) => {
|
||||||
return {value: x, name: x};
|
return {value: x, name: x};
|
||||||
|
|||||||
51
src/views/Login.vue
Normal file
51
src/views/Login.vue
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<script setup>
|
||||||
|
import {Input} from 'vuedigitalpowerui'
|
||||||
|
import {onMounted, ref} from "vue";
|
||||||
|
import {useUserStore} from "@/pinia/user";
|
||||||
|
import {useRouter} from "vue-router";
|
||||||
|
import {show} from "@/services/notification";
|
||||||
|
|
||||||
|
const email = ref();
|
||||||
|
const password = ref();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
localStorage.clear();
|
||||||
|
userStore.user = null;
|
||||||
|
})
|
||||||
|
|
||||||
|
async function login() {
|
||||||
|
await userStore.login(email.value, password.value);
|
||||||
|
if (userStore.user) {
|
||||||
|
show("Bienvenido " + userStore.user.name, "/login.mp4");
|
||||||
|
setTimeout(() => {
|
||||||
|
router.push("/");
|
||||||
|
}, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div id="app-layout" flex flex-center fade>
|
||||||
|
<form @submit.prevent="login" shadow radius-border="">
|
||||||
|
<h1>Iniciar Sesion</h1>
|
||||||
|
<p>Ingrese sus credenciales para continuar</p>
|
||||||
|
<Input v-model="email" type="text" label="Email"/>
|
||||||
|
<Input v-model="password" class="mt-3" type="password" label="Contraseña"/>
|
||||||
|
<Input class="mt-4" type="button" value="Ingresar" background="var(--primary)" color="white" @click="login"/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
form {
|
||||||
|
width: 600px;
|
||||||
|
height: 450px;
|
||||||
|
padding: 4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app-layout {
|
||||||
|
height: calc(90vh - var(--header-height) + 60px);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user