Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c7d03346ee | |||
| 1d66aaf7c5 | |||
| 9850887285 | |||
| 9dff07dc0a |
15
electron.js
15
electron.js
@@ -1,18 +1,22 @@
|
|||||||
const { app, BrowserWindow } = require('electron');
|
const {app, BrowserWindow} = require('electron');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
function createWindow() {
|
function createWindow() {
|
||||||
const win = new BrowserWindow({
|
const win = new BrowserWindow({
|
||||||
width: 1024,
|
width: 1100,
|
||||||
height: 720,
|
height: 790,
|
||||||
|
resizable: false,
|
||||||
|
frame: false,
|
||||||
|
transparent: true,
|
||||||
|
autoHideMenuBar: true,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
preload: path.join(__dirname, 'preload.js'),
|
preload: path.join(__dirname, 'preload.js'),
|
||||||
nodeIntegration: true,
|
nodeIntegration: true,
|
||||||
contextIsolation: false
|
contextIsolation: false,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
win.loadURL('http://localhost:8080');
|
win.loadURL('http://localhost:8080');
|
||||||
//win.loadFile(path.join(__dirname, 'dist/index.html'));
|
// win.loadFile(path.join(__dirname, 'dist/index.html'));
|
||||||
}
|
}
|
||||||
|
|
||||||
app.whenReady().then(createWindow);
|
app.whenReady().then(createWindow);
|
||||||
@@ -21,7 +25,6 @@ app.on('window-all-closed', () => {
|
|||||||
app.quit();
|
app.quit();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on('activate', () => {
|
app.on('activate', () => {
|
||||||
if (BrowserWindow.getAllWindows().length === 0) {
|
if (BrowserWindow.getAllWindows().length === 0) {
|
||||||
createWindow();
|
createWindow();
|
||||||
|
|||||||
567
package-lock.json
generated
567
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
@@ -21,16 +21,22 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"axios": "^1.7.7",
|
||||||
|
"bootstrap": "^5.3.3",
|
||||||
|
"boxicons": "^2.1.4",
|
||||||
"pinia": "^2.2.2",
|
"pinia": "^2.2.2",
|
||||||
"vue": "^3.2.13",
|
"vue": "^3.2.13",
|
||||||
"vue-router": "^4.0.3"
|
"vue-router": "^4.0.3",
|
||||||
|
"vuedigitalpowerui": "^0.1.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-router": "~5.0.0",
|
"@vue/cli-plugin-router": "~5.0.0",
|
||||||
"@vue/cli-service": "~5.0.0",
|
"@vue/cli-service": "~5.0.0",
|
||||||
"electron": "^32.1.2",
|
"electron": "^32.1.2",
|
||||||
"electron-builder": "^25.0.5",
|
"electron-builder": "^25.0.5",
|
||||||
"sass": "^1.32.7",
|
"jquery": "^3.7.1",
|
||||||
"sass-loader": "^12.0.0"
|
"popper.js": "^1.16.1",
|
||||||
|
"sass": "^1.79.3",
|
||||||
|
"sass-loader": "^12.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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.
106
src/App.vue
106
src/App.vue
@@ -1,15 +1,25 @@
|
|||||||
<template>
|
<template>
|
||||||
<nav>
|
<head>
|
||||||
<router-link to="/">Home</router-link> |
|
<link rel="stylesheet" href="https://digitalpower.ar/cdn/digitalpower.css">
|
||||||
<router-link to="/about">About</router-link>
|
</head>
|
||||||
</nav>
|
<div class="window-container">
|
||||||
<router-view />
|
<div id="title-bar" noselect></div>
|
||||||
|
<div id="notification-container" noselect></div>
|
||||||
|
<Intro v-if="ui.intro"/>
|
||||||
|
|
||||||
|
<Nav/>
|
||||||
|
<router-view/>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
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 Intro from "@/components/app/Intro.vue";
|
||||||
|
import {useUi} from "@/pinia/ui";
|
||||||
|
|
||||||
|
const ui = useUi()
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -18,6 +28,16 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
:root {
|
||||||
|
--header-height: 150px !important;
|
||||||
|
--app-shadow: rgba(0, 0, 0, 0.45);
|
||||||
|
--navmenu-height: 210px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
#app {
|
#app {
|
||||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
@@ -26,6 +46,23 @@ onMounted(() => {
|
|||||||
color: #2c3e50;
|
color: #2c3e50;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#title-bar {
|
||||||
|
-webkit-app-region: drag;
|
||||||
|
height: 20px;
|
||||||
|
background: var(--primary);
|
||||||
|
color: white;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notification-container {
|
||||||
|
top: unset !important;
|
||||||
|
bottom: 2em !important;
|
||||||
|
margin-top: 6em;
|
||||||
|
right: 4% !important;
|
||||||
|
}
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
padding: 30px;
|
padding: 30px;
|
||||||
|
|
||||||
@@ -38,4 +75,61 @@ nav {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
padding: 2em;
|
||||||
|
width: 1024px;
|
||||||
|
height: 720px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
text-align: left !important;
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-container {
|
||||||
|
box-shadow: 0px 0px 20px 0px #0a0a0a;
|
||||||
|
border-radius: 15px;
|
||||||
|
width: 1024px;
|
||||||
|
height: 720px;
|
||||||
|
background: white;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-wrapper::after {
|
||||||
|
content: ">" !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideOut {
|
||||||
|
from {
|
||||||
|
transform: translateX(0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-leave-active {
|
||||||
|
transition: opacity var(--duration), transform var(--duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-leave-to {
|
||||||
|
animation: slideOut var(--duration) forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[selectedNav="true"] {
|
||||||
|
background: white;
|
||||||
|
|
||||||
|
& > p {
|
||||||
|
color: var(--text-color) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
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">La fabrica de aplicaciones <br> Web</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>
|
||||||
28
src/components/app/Layout.vue
Normal file
28
src/components/app/Layout.vue
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<script setup>
|
||||||
|
import {useUserStore} from "@/pinia/user";
|
||||||
|
import {onBeforeMount} from "vue";
|
||||||
|
import {useRoute, useRouter} from "vue-router";
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
onBeforeMount(async () => {
|
||||||
|
const user = await userStore.getUser();
|
||||||
|
if (!user && !["/#/login", "/#/register"].includes(route.fullPath)) {
|
||||||
|
location.href = "/#/login";
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div id="app-layout">
|
||||||
|
<slot v-if="userStore.user"></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
#app-layout {
|
||||||
|
padding: 1em 2em;
|
||||||
|
height: calc(100vh - var(--header-height));
|
||||||
|
}
|
||||||
|
</style>
|
||||||
157
src/components/app/Nav.vue
Normal file
157
src/components/app/Nav.vue
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
<script setup>
|
||||||
|
import NavMenu from "@/components/app/NavMenu.vue";
|
||||||
|
import {onMounted, ref} from "vue";
|
||||||
|
import {useUi} from "@/pinia/ui";
|
||||||
|
import {useRouter} from "vue-router";
|
||||||
|
import {useUserStore} from "@/pinia/user";
|
||||||
|
import {useProductStore} from "@/pinia/products";
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const ui = useUi();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const productStore = useProductStore();
|
||||||
|
const options = ref([
|
||||||
|
{
|
||||||
|
name: "Aplicacion", items: [
|
||||||
|
{name: "Manual"},
|
||||||
|
{name: "Solicitar Ayuda"},
|
||||||
|
{name: "Digital Power"},
|
||||||
|
{
|
||||||
|
name: "Cerrar sesion",
|
||||||
|
callback: () => {
|
||||||
|
localStorage.clear();
|
||||||
|
ui.reset();
|
||||||
|
router.push("/login");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Cerrar",
|
||||||
|
callback: () => {
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Productos", items: [
|
||||||
|
{
|
||||||
|
name: "Agregar Producto", callback: () => {
|
||||||
|
ui.reset()
|
||||||
|
productStore.product_modal = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{name: "Generar Venta"},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Imprimir",
|
||||||
|
items: [
|
||||||
|
{name: "Listado de productos"},
|
||||||
|
{name: "Estadisticas"},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Estadisticas",
|
||||||
|
items: [],
|
||||||
|
url: '/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Chat",
|
||||||
|
items: [],
|
||||||
|
url: '/',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
function openMenu(items, name, url) {
|
||||||
|
if (ui.selectedMenu === name) return;
|
||||||
|
|
||||||
|
|
||||||
|
ui.setCurrentMenu(null);
|
||||||
|
if (!items) return;
|
||||||
|
if (url) {
|
||||||
|
router.push(url);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
ui.setCurrentMenu(items);
|
||||||
|
ui.selectedMenu = name;
|
||||||
|
}, 5)
|
||||||
|
setTimeout(() => {
|
||||||
|
let $menu = document.querySelector("#menu-dropdown");
|
||||||
|
let $option = document.querySelector("#" + name);
|
||||||
|
const rect = ($option.getBoundingClientRect())
|
||||||
|
|
||||||
|
$menu.setAttribute("style", `
|
||||||
|
position: fixed;
|
||||||
|
left: ${rect.left}px;
|
||||||
|
bottom: ${rect.bottom}px;
|
||||||
|
top: ${rect.top + rect.height}px;
|
||||||
|
`)
|
||||||
|
}, 20)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClick(item) {
|
||||||
|
item?.callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<nav v-if="userStore.user">
|
||||||
|
<div class="list" noselect>
|
||||||
|
<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>
|
||||||
|
{{
|
||||||
|
opt?.name
|
||||||
|
}}</p></div>
|
||||||
|
</div>
|
||||||
|
<NavMenu @callback="handleClick" v-if="ui.currentMenu"></NavMenu>
|
||||||
|
</nav>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
nav {
|
||||||
|
background: var(--primary);
|
||||||
|
width: 100%;
|
||||||
|
height: 60px;
|
||||||
|
padding: 0;
|
||||||
|
z-index: 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: start;
|
||||||
|
align-items: end;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
transition: var(--duration);
|
||||||
|
border-top-right-radius: 15px;
|
||||||
|
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background: white;
|
||||||
|
|
||||||
|
& > p {
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > p {
|
||||||
|
transition: var(--duration);
|
||||||
|
margin: 0;
|
||||||
|
padding: 1em;
|
||||||
|
border-top-right-radius: 15px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
58
src/components/app/NavMenu.vue
Normal file
58
src/components/app/NavMenu.vue
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<script setup>
|
||||||
|
import {useUi} from "@/pinia/ui";
|
||||||
|
|
||||||
|
const emits = defineEmits(["callback"]);
|
||||||
|
const ui = useUi();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="nav-menu" id="menu-dropdown">
|
||||||
|
<div v-for="(item, key) in ui.currentMenu" :key class="item" @click="emits('callback', item)">
|
||||||
|
{{ item?.name }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.nav-menu {
|
||||||
|
width: 250px;
|
||||||
|
height: var(--navmenu-height);
|
||||||
|
|
||||||
|
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.4);
|
||||||
|
background: white;
|
||||||
|
border-bottom-left-radius: 15px;
|
||||||
|
border-bottom-right-radius: 15px;
|
||||||
|
|
||||||
|
animation: fade .2s ease-in-out forwards;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
padding: .5em 1em;
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.2);
|
||||||
|
text-align: left;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
transition: var(--duration);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: white;
|
||||||
|
background: var(--primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
height: 0
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
height: var(--navmenu-height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
42
src/components/productos/Filters.vue
Normal file
42
src/components/productos/Filters.vue
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<script setup>
|
||||||
|
import {Input} from 'vuedigitalpowerui'
|
||||||
|
import {ref} from "vue";
|
||||||
|
import {useProductStore} from "@/pinia/products";
|
||||||
|
|
||||||
|
const emits = defineEmits(["search"]);
|
||||||
|
const props = defineProps(["filters"])
|
||||||
|
const filter = ref("");
|
||||||
|
const value = ref("");
|
||||||
|
|
||||||
|
const productStore = useProductStore()
|
||||||
|
|
||||||
|
function search() {
|
||||||
|
emits("search", {filter: filter.value, value: value.value})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function update() {
|
||||||
|
await productStore.fetchProducts();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div id="filters" class="mt-3">
|
||||||
|
<form @submit.prevent="search" flex gapped flex-wrap>
|
||||||
|
<Input class="filter" type="select" v-model="filter" :options="filters"/>
|
||||||
|
<Input class="filter" placeholder="Busqueda" v-model="value"/>
|
||||||
|
<Input type="button" value="Buscar" background="var(--primary)" color="white" @click="search"/>
|
||||||
|
<Input type="button" value="Limpiar" background="var(--primary)" color="white"/>
|
||||||
|
<Input type="button" value="Actualizar" background="var(--primary)" color="white" @click="update"/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
#filters {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter {
|
||||||
|
width: 250px !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
38
src/components/productos/ProductModal.vue
Normal file
38
src/components/productos/ProductModal.vue
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<script setup>
|
||||||
|
import {Modal, Input} from 'vuedigitalpowerui'
|
||||||
|
import {useProductStore} from "@/pinia/products";
|
||||||
|
import {reactive} from "vue";
|
||||||
|
|
||||||
|
const productStore = useProductStore()
|
||||||
|
const props = defineProps(["headers"])
|
||||||
|
const emits = defineEmits(["confirm"])
|
||||||
|
const newProduct = reactive({});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal title="Producto" @close="() => {productStore.product_modal = false}">
|
||||||
|
<div class="entradas" gapped>
|
||||||
|
<div v-for="(item, key) in headers" class="entrada">
|
||||||
|
<Input class="mt-3" :key :label="item?.name" v-if="item?.name !== 'acciones'" v-model="newProduct[item?.name]"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div flex gapped class="mt-3">
|
||||||
|
<Input value="Guardar" type="button" background="var(--primary)" color="white"
|
||||||
|
@click="() => emits('confirm', newProduct)"/>
|
||||||
|
<Input value="Cancelar" type="button" background="var(--red)" color="white"
|
||||||
|
@click="() => {productStore.product_modal = false}"/>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.entradas {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entrada {
|
||||||
|
width: 47%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
96
src/components/productos/Table.vue
Normal file
96
src/components/productos/Table.vue
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
<script setup>
|
||||||
|
import {useUi} from "@/pinia/ui";
|
||||||
|
import {onMounted} from "vue";
|
||||||
|
import {useProductStore} from "@/pinia/products";
|
||||||
|
import {limit} from "../../services/utils";
|
||||||
|
|
||||||
|
const props = defineProps(["items", "headers"])
|
||||||
|
const productStore = useProductStore();
|
||||||
|
onMounted(async () => {
|
||||||
|
await productStore.fetchProducts();
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div id="table-container" radius-border fade>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th noselect v-for="(head, key) in headers" :key>{{ head?.name }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="(item, key) in productStore.productos">
|
||||||
|
<td noselect v-for="(head, key) in headers" :class="head">{{
|
||||||
|
limit(item?.[head?.name?.toLowerCase()], 30)
|
||||||
|
}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
#table-container {
|
||||||
|
box-shadow: 0px 0px 5px 0px var(--app-shadow);
|
||||||
|
border-radius: 15px;
|
||||||
|
height: 80%;
|
||||||
|
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead > tr > th {
|
||||||
|
border-top: none;
|
||||||
|
text-transform: capitalize;
|
||||||
|
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody > tr {
|
||||||
|
transition: var(--duration);
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
& > td {
|
||||||
|
border-bottom: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--primary);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
padding: 1em 2em;
|
||||||
|
//border: 1px solid var(--text-color);
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.27);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
padding: 1em 2em;
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.27);
|
||||||
|
}
|
||||||
|
|
||||||
|
.Nombre {
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -2,7 +2,13 @@ import {createApp} from 'vue'
|
|||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
import {createPinia} from "pinia";
|
import {createPinia} from "pinia";
|
||||||
|
import "../src/styles/digitalpower.css"
|
||||||
|
import 'boxicons'
|
||||||
|
import * as bootstrap from 'bootstrap'
|
||||||
|
import 'bootstrap'
|
||||||
|
import 'bootstrap/dist/css/bootstrap.min.css'
|
||||||
|
import 'jquery/src/jquery.js'
|
||||||
|
import 'bootstrap/dist/js/bootstrap.min.js'
|
||||||
|
|
||||||
const pinia = createPinia();
|
const pinia = createPinia();
|
||||||
const app = createApp(App).use(pinia).use(router).mount('#app')
|
const app = createApp(App).use(pinia).use(router).mount('#app')
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
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: {},
|
||||||
|
product_modal: false,
|
||||||
}),
|
}),
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
21
src/pinia/ui.js
Normal file
21
src/pinia/ui.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import {defineStore} from "pinia";
|
||||||
|
|
||||||
|
export const useUi = defineStore('ui', {
|
||||||
|
state: () => ({
|
||||||
|
currentMenu: null,
|
||||||
|
selectedMenu: null,
|
||||||
|
intro: true,
|
||||||
|
login: true,
|
||||||
|
}),
|
||||||
|
getters: {},
|
||||||
|
actions: {
|
||||||
|
setCurrentMenu(state) {
|
||||||
|
if (!state) this.selectedMenu = null;
|
||||||
|
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,4 +1,4 @@
|
|||||||
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 = [
|
||||||
@@ -8,19 +8,14 @@ const routes = [
|
|||||||
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
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
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;
|
||||||
|
}
|
||||||
12
src/services/utils.js
Normal file
12
src/services/utils.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
export const limit = (text, limit) => {
|
||||||
|
if (!text) return "";
|
||||||
|
if (text?.length <= limit) {
|
||||||
|
return text;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return text?.slice(0, limit) + '...';
|
||||||
|
} catch (e) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
663
src/styles/digitalpower.css
Normal file
663
src/styles/digitalpower.css
Normal file
@@ -0,0 +1,663 @@
|
|||||||
|
@import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap");
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-family: "Poppins", sans-serif;
|
||||||
|
/* Esto afectará a cualquier scrollbar en tu página web */
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 3px;
|
||||||
|
height: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Track */
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: var(--gray-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle */
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle on hover */
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--gray: rgba(0, 0, 0, 0.3);
|
||||||
|
--gray-light: rgba(0, 0, 0, 0.1);
|
||||||
|
--gray-very-light: rgba(0, 0, 0, 0.08);
|
||||||
|
--primary-blue: #3a7ca5;
|
||||||
|
--primary: dodgerblue;
|
||||||
|
--primary-active-item: #efefef;
|
||||||
|
--secondary: #0887a1;
|
||||||
|
--red: #d14c4c;
|
||||||
|
--back-color: white;
|
||||||
|
--text-color: rgba(62, 62, 62, 0.7);
|
||||||
|
--sidebar-color: dodgerblue;
|
||||||
|
--sidebar-color-hover: white;
|
||||||
|
--input-bg: white;
|
||||||
|
--input-button: #efefef;
|
||||||
|
--input-bg-hover: #dbdbdb;
|
||||||
|
--shadow: 0px 0px 5px 0px var(--gray);
|
||||||
|
|
||||||
|
--admin-title-height: 67px;
|
||||||
|
--duration: 0.3s;
|
||||||
|
|
||||||
|
--header-height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
overflow-x: hidden;
|
||||||
|
background: var(--back-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#app-layout {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notification-container {
|
||||||
|
position: fixed;
|
||||||
|
top: calc(var(--header-height) + 1.5em);
|
||||||
|
right: 0;
|
||||||
|
margin-right: 1em;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, p, b {
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background: lightgray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-enter-active,
|
||||||
|
.page-leave-active {
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-enter-from,
|
||||||
|
.page-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-modal-html {
|
||||||
|
width: 80vw;
|
||||||
|
height: fit-content;
|
||||||
|
max-height: 100vh;
|
||||||
|
max-width: 100vh;
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
z-index: 200000;
|
||||||
|
color: var(--text-color);
|
||||||
|
background: var(--back-color);
|
||||||
|
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
|
||||||
|
.title {
|
||||||
|
padding: 0.5em;
|
||||||
|
border-bottom: 1px solid var(--gray);
|
||||||
|
|
||||||
|
h5,
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 2em;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
gap: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: dodgerblue;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active-item {
|
||||||
|
background: var(--secondary);
|
||||||
|
color: var(--sidebar-color-hover) !important;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
fill: var(--sidebar-color-hover) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification {
|
||||||
|
padding: 1em;
|
||||||
|
background: var(--primary);
|
||||||
|
color: white;
|
||||||
|
margin: 1em 0;
|
||||||
|
min-width: 200px;
|
||||||
|
|
||||||
|
pointer-events: none;
|
||||||
|
user-select: none;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-enter {
|
||||||
|
animation-name: notificationAnimationEnter;
|
||||||
|
animation-duration: 0.25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-exit {
|
||||||
|
animation-name: notificationAnimationExit;
|
||||||
|
animation-duration: 0.25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ck-editor {
|
||||||
|
display: block;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ck {
|
||||||
|
color: var(--text-color) !important;
|
||||||
|
background: var(--input-bg) !important;
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.red {
|
||||||
|
background: var(--red) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
[limited-text] {
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
text-wrap: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
[flex] {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
[flex-wrap] {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
[nopointer] {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[subitem] {
|
||||||
|
padding-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
[groupitem] {
|
||||||
|
border-bottom: 1px solid var(--gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
[grouplabel] {
|
||||||
|
background: var(--gray-light);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
[menuitem] {
|
||||||
|
border-bottom: 1px solid var(--gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
[menuitem],
|
||||||
|
[subitem] {
|
||||||
|
transition: 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--secondary);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[shadow] {
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
[borderbottom] {
|
||||||
|
border-bottom: 1px solid var(--gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
[bordered] {
|
||||||
|
border: 1px solid var(--gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
[radius-border] {
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[bound] {
|
||||||
|
background: var(--gray-light);
|
||||||
|
background-image: linear-gradient(
|
||||||
|
to right,
|
||||||
|
transparent 20%,
|
||||||
|
var(--gray-very-light) 60%
|
||||||
|
);
|
||||||
|
background-size: 200% 100%;
|
||||||
|
transition: background-position 0.2s ease-out;
|
||||||
|
animation: boundAnimation 2s ease infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
[custominput] {
|
||||||
|
border: 1px solid var(--gray-light);
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
border: 1px solid var(--gray);
|
||||||
|
box-shadow: 0px 0px 5px 0px var(--gray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[customselect] {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
select {
|
||||||
|
padding: 0.6em 1em;
|
||||||
|
width: 100%;
|
||||||
|
appearance: none;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-wrapper {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: "â–¼";
|
||||||
|
font-size: 1rem;
|
||||||
|
top: 50%;
|
||||||
|
right: 1em;
|
||||||
|
position: absolute;
|
||||||
|
color: var(--gray);
|
||||||
|
|
||||||
|
transform: translateY(-50%);
|
||||||
|
|
||||||
|
z-index: 100000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-button-html {
|
||||||
|
padding: 0.6em 1.5em;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1em;
|
||||||
|
|
||||||
|
display: block;
|
||||||
|
width: fit-content;
|
||||||
|
|
||||||
|
transition: 0.2s;
|
||||||
|
|
||||||
|
color: white;
|
||||||
|
background: var(--primary);
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
filter: brightness(0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[flex-center] {
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
[flex-start] {
|
||||||
|
display: flex !important;
|
||||||
|
align-items: start;
|
||||||
|
justify-content: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
[flex-column] {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
[block-center] {
|
||||||
|
display: block;
|
||||||
|
margin: auto;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
[overlay] {
|
||||||
|
position: fixed;
|
||||||
|
top: 2em !important;
|
||||||
|
left: 2em !important;
|
||||||
|
width: calc(100vw - 4.6em) !important;
|
||||||
|
height: calc(100vh - 4.2em) !important;
|
||||||
|
|
||||||
|
z-index: 15;
|
||||||
|
background: var(--gray);
|
||||||
|
border-radius: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[inside-verlay] {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
display: none;
|
||||||
|
transition: var(--duration);
|
||||||
|
|
||||||
|
z-index: 15;
|
||||||
|
background: rgba(0, 0, 0, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
[fade] {
|
||||||
|
animation-name: fadeAnim;
|
||||||
|
animation-duration: .5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
[delayedfade] {
|
||||||
|
animation: fadeAnim 0.2s forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[absolute-centered] {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
[list] {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[gapped] {
|
||||||
|
gap: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
[noselect] {
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[underline] {
|
||||||
|
/*
|
||||||
|
border-bottom: 1px solid var(--primary);
|
||||||
|
|
||||||
|
*/
|
||||||
|
padding-bottom: 10px;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 2px;
|
||||||
|
background: var(--primary);
|
||||||
|
|
||||||
|
animation-name: underlineAnim;
|
||||||
|
animation-duration: var(--duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[nowrap] {
|
||||||
|
text-wrap: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
[pointer] {
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
|
|
||||||
|
[select] {
|
||||||
|
user-select: all;
|
||||||
|
}
|
||||||
|
|
||||||
|
[slide] {
|
||||||
|
animation: slide;
|
||||||
|
animation-duration: var(--duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
[slideRight] {
|
||||||
|
animation: slideRight;
|
||||||
|
animation-duration: var(--duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
[slideUp] {
|
||||||
|
animation: slideUp;
|
||||||
|
animation-duration: var(--duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
[slideDown] {
|
||||||
|
animation: slideDown;
|
||||||
|
animation-duration: var(--duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
[fixed] {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 100;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
border: 3px solid;
|
||||||
|
border-color: #fff #fff transparent transparent;
|
||||||
|
box-sizing: border-box;
|
||||||
|
animation: rotation 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader::after,
|
||||||
|
.loader::before {
|
||||||
|
content: "";
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin: auto;
|
||||||
|
border: 3px solid;
|
||||||
|
border-color: transparent transparent var(--primary-blue) var(--primary-blue);
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
animation: rotationBack 0.5s linear infinite;
|
||||||
|
transform-origin: center center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader::before {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-color: #fff #fff transparent transparent;
|
||||||
|
animation: rotation 1.5s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@keyframes rotation {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rotationBack {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(-360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rotate {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes prixClipFix {
|
||||||
|
0% {
|
||||||
|
clip-path: polygon(50% 50%, 0 0, 0 0, 0 0, 0 0, 0 0);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
clip-path: polygon(50% 50%, 0 0, 100% 0, 100% 0, 100% 0, 100% 0);
|
||||||
|
}
|
||||||
|
75%,
|
||||||
|
100% {
|
||||||
|
clip-path: polygon(50% 50%, 0 0, 100% 0, 100% 100%, 100% 100%, 100% 100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes boundAnimation {
|
||||||
|
0% {
|
||||||
|
background-position: 0 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: 100% 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes notificationAnimationEnter {
|
||||||
|
0% {
|
||||||
|
transform: translateX(150%);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translateX(0%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes notificationAnimationExit {
|
||||||
|
0% {
|
||||||
|
transform: translateX(0%);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translateX(150%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeAnim {
|
||||||
|
0% {
|
||||||
|
opacity: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes underlineAnim {
|
||||||
|
0% {
|
||||||
|
width: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slide {
|
||||||
|
0% {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translateX(0%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideRight {
|
||||||
|
0% {
|
||||||
|
transform: translateX(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translateX(0%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideUp {
|
||||||
|
0% {
|
||||||
|
transform: translateY(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translateY(0%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideDown {
|
||||||
|
0% {
|
||||||
|
transform: translateY(-100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translateY(0%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1000px) {
|
||||||
|
.dp-container {
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-modal-html {
|
||||||
|
height: 60vh;
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5em !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,55 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="home">
|
<Layout @click="handleClick">
|
||||||
<img alt="Vue logo" src="../assets/logo.png">
|
<Table :headers :items="productos"></Table>
|
||||||
<HelloWorld msg="Welcome to Your Vue.js App"/>
|
<Filters :filters="headers"></Filters>
|
||||||
</div>
|
<ProductModal v-if="productStore.product_modal" :headers @confirm="saveProduct"/>
|
||||||
|
</Layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import HelloWorld from '@/components/HelloWorld.vue'
|
import Layout from "@/components/app/Layout.vue";
|
||||||
|
import Table from "@/components/productos/Table.vue";
|
||||||
|
import {computed, onMounted, ref} from "vue";
|
||||||
|
import Filters from "@/components/productos/Filters.vue";
|
||||||
|
import {useUi} from "@/pinia/ui";
|
||||||
|
import {useUserStore} from "@/pinia/user";
|
||||||
|
import ProductModal from "@/components/productos/ProductModal.vue";
|
||||||
|
import {useProductStore} from "@/pinia/products";
|
||||||
|
import {DBService, post} from "@/services/apiReq";
|
||||||
|
|
||||||
|
const ui = useUi();
|
||||||
|
const productStore = useProductStore()
|
||||||
|
const headers = computed(() => {
|
||||||
|
const producto = productStore.productos?.[0];
|
||||||
|
if (!producto) return [
|
||||||
|
{value: 'Nombre', name: 'Nombre'},
|
||||||
|
{value: 'Precio', name: 'Precio'},
|
||||||
|
{value: 'Stock', name: 'Stock'},
|
||||||
|
{value: 'Vendidos', name: 'Vendidos'},
|
||||||
|
{value: 'Acciones', name: 'Acciones'},
|
||||||
|
];
|
||||||
|
|
||||||
|
delete producto.id;
|
||||||
|
producto.acciones = null;
|
||||||
|
|
||||||
|
return Object.keys(producto).map((x) => {
|
||||||
|
return {value: x, name: x};
|
||||||
|
})
|
||||||
|
})
|
||||||
|
const productos = ref([
|
||||||
|
{Nombre: "Producto de prueba de longitud", Precio: "Precio", Stock: "Stock", Vendidos: 0, Acciones: 0},
|
||||||
|
]);
|
||||||
|
|
||||||
|
function handleClick() {
|
||||||
|
ui.setCurrentMenu(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveProduct(product) {
|
||||||
|
let response = await post('/insert', {
|
||||||
|
table: 'productos',
|
||||||
|
data: product,
|
||||||
|
}, DBService);
|
||||||
|
productStore.product_modal = false;
|
||||||
|
await productStore.fetchProducts();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
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