refactor: #6555 code translation JQuery to Js
gitea/worker-time-control/pipeline/pr-test This commit looks good
Details
gitea/worker-time-control/pipeline/pr-test This commit looks good
Details
This commit is contained in:
parent
b2fa1d341b
commit
213b4f8520
|
@ -11,7 +11,6 @@ and open the template in the editor.
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="css/style.css" rel="stylesheet" type="text/css"/>
|
||||
<link rel="icon" type="image/png" href="img/favicon.ico">
|
||||
<script src="node_modules/jquery/dist/jquery.min.js" type="text/javascript"></script>
|
||||
<script src="node_modules/fastclick/lib/fastclick.js" type="text/javascript"></script>
|
||||
</head>
|
||||
<body class="pinLogin">
|
||||
|
|
|
@ -202,7 +202,7 @@ h3 {
|
|||
text-align: center;
|
||||
}
|
||||
.footer {
|
||||
animation: faceIn 0.2s ease-in-out;
|
||||
animation: fadeIn 0.2s ease-in-out;
|
||||
}
|
||||
.in, .inMiddle {
|
||||
display: inline;
|
||||
|
@ -372,6 +372,15 @@ header {
|
|||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
display: block;
|
||||
animation: fadeIn 0.2s ease-in-out forwards;
|
||||
}
|
||||
|
||||
.fade-out {
|
||||
animation: fadeOut 0.2s ease-in-out forwards;
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
0% {
|
||||
transform: translateY(-100%);
|
||||
|
@ -382,7 +391,8 @@ header {
|
|||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@keyframes faceIn {
|
||||
|
||||
@keyframes fadeIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
@ -391,10 +401,21 @@ header {
|
|||
}
|
||||
}
|
||||
|
||||
@keyframes fadeOut {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
src: url('../font/Poppins.ttf') format('truetype');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
src: url('../font/Poppins-Bold.ttf') format('truetype');
|
||||
|
|
|
@ -11,7 +11,6 @@ and open the template in the editor.
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="css/style.css" rel="stylesheet" type="text/css"/>
|
||||
<link rel="icon" type="image/png" href="img/favicon.ico">
|
||||
<script src="node_modules/jquery/dist/jquery.min.js" type="text/javascript"></script>
|
||||
<script src="node_modules/fastclick/lib/fastclick.js" type="text/javascript"></script>
|
||||
</head>
|
||||
<body class="pinLogin">
|
||||
|
@ -80,7 +79,7 @@ and open the template in the editor.
|
|||
</div>
|
||||
</div>
|
||||
<h1>
|
||||
developed by Verdnatura with<span class="heart"></span>
|
||||
powered by Verdnatura with<span class="heart"></span>
|
||||
</h1>
|
||||
<h2 class="device">
|
||||
<p id="deviceLabel"></p>
|
||||
|
|
286
js/clockIn.js
286
js/clockIn.js
|
@ -1,237 +1,139 @@
|
|||
let userData = "";
|
||||
const footer = document.querySelector(".footer");
|
||||
const txtName = document.querySelector("#txtNombre");
|
||||
const inBtn = document.querySelector(".in");
|
||||
const inMiddleBtn = document.querySelector(".inMiddle");
|
||||
const outMiddleBtn = document.querySelector(".outMiddle");
|
||||
const outBtn = document.querySelector(".out");
|
||||
const timetableList = document.querySelector(".listHorario");
|
||||
const weekDays = ["Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado"];
|
||||
|
||||
$(document).ready(function () {
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
userData = JSON.parse(localStorage.getItem("userData"));
|
||||
FastClick.attach(document.body);
|
||||
setView();
|
||||
setEvents();
|
||||
});
|
||||
|
||||
function setEvents() {
|
||||
document.querySelector(".btnSalir").addEventListener("click", close);
|
||||
|
||||
$(".btnSalir").on("click", function () {
|
||||
cerrar();
|
||||
});
|
||||
inBtn.addEventListener("click", () => clockIn("in"));
|
||||
inMiddleBtn.addEventListener("click", () => clockIn("middle"));
|
||||
outMiddleBtn.addEventListener("click", () => clockIn("middle"));
|
||||
outBtn.addEventListener("click", () => clockIn("out"));
|
||||
|
||||
$(".in").on("click", function () {
|
||||
fichar('in');
|
||||
});
|
||||
|
||||
$(".inMiddle").on("click", function () {
|
||||
fichar('middle');
|
||||
});
|
||||
|
||||
$(".outMiddle").on("click", function () {
|
||||
fichar('middle');
|
||||
});
|
||||
|
||||
$(".out").on("click", function () {
|
||||
fichar('out');
|
||||
});
|
||||
|
||||
setTimeout(function () {
|
||||
cerrar();
|
||||
}, 5000);
|
||||
// setTimeout(close, 5000);
|
||||
}
|
||||
|
||||
function setView() {
|
||||
$(".footer").hide();
|
||||
$("#txtNombre").text(userData["name"] + " " + userData["surname"]);
|
||||
getInfo();
|
||||
$("." + userData["button1"]).show();
|
||||
$("." + userData["button2"]).show();
|
||||
}
|
||||
|
||||
function fichar(direction) {
|
||||
const data = {
|
||||
workerFk: userData['userFk'],
|
||||
async function clockIn(direction) {
|
||||
let timeout = 1000;
|
||||
const data = await call("WorkerTimeControls/clockIn", {
|
||||
method: "POST",
|
||||
body: {
|
||||
workerFk: userData["userFk"],
|
||||
direction,
|
||||
device: localStorage.getItem("device")
|
||||
device: localStorage.getItem("device"),
|
||||
},
|
||||
});
|
||||
|
||||
if (data.error) {
|
||||
let msg = "";
|
||||
for (const val of data) if (val.error) msg += `${val.error}\n`;
|
||||
printError(msg);
|
||||
} else {
|
||||
document.querySelector(".confirm").classList.add("fade-in");
|
||||
txtConfirm.textContent = "Fichada registrada correctamente";
|
||||
timeout = 2000;
|
||||
}
|
||||
|
||||
$.post({
|
||||
urlPath: 'WorkerTimeControls/clockIn',
|
||||
jsonData: data,
|
||||
processData: false,
|
||||
success: function (msg) {
|
||||
if (msg.error){
|
||||
printErrores(msg);
|
||||
setTimeout(function () {
|
||||
cerrar();
|
||||
}, 2000);
|
||||
}else {
|
||||
$(".confirm").fadeIn(200);
|
||||
$(".txtConfirm").append('Fichada registrada correctamente');
|
||||
setTimeout(function () {
|
||||
cerrar();
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
});
|
||||
setTimeout(close, timeout);
|
||||
}
|
||||
|
||||
function setView() {
|
||||
$(".footer").hide();
|
||||
$(".in").hide();
|
||||
$(".inMiddle").hide();
|
||||
$(".outMiddle").hide();
|
||||
$(".out").hide();
|
||||
$("#txtNombre").text(userData["name"] + " " + userData["surname"]);
|
||||
footer.style.display = "none";
|
||||
inBtn.style.display = "none";
|
||||
inMiddleBtn.style.display = "none";
|
||||
outMiddleBtn.style.display = "none";
|
||||
outBtn.style.display = "none";
|
||||
|
||||
txtName.textContent = `${userData.name} ${userData.surname}`;
|
||||
getInfo();
|
||||
if(userData["button1"] === null && userData["button2"] === null ) {
|
||||
printError ("Contacta con tu responsable")
|
||||
} else {
|
||||
$("." + userData["button1"]).show();
|
||||
$("." + userData["button2"]).show();
|
||||
}
|
||||
|
||||
if (userData.button1 === null && userData.button2 === null) return printError("Contacta con tu responsable");
|
||||
|
||||
document.querySelector(`.${userData.button1}`).style.display = "inline-block";
|
||||
if (userData.button2) document.querySelector(`.${userData.button2}`).style.display = "inline-block";
|
||||
}
|
||||
|
||||
function getInfo() {
|
||||
const queryString = $.param({ workerFk: userData['userFk'] });
|
||||
|
||||
$.get({
|
||||
urlPath: `WorkerTimeControls/getClockIn?${queryString}`,
|
||||
processData: false,
|
||||
success: function (data) {
|
||||
$('.footer').show();
|
||||
async function getInfo() {
|
||||
const data = await call("WorkerTimeControls/getClockIn", { params: { workerFk: userData["userFk"] } });
|
||||
footer.style.display = "block";
|
||||
printTimetable(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function printTimetable(timetable) {
|
||||
const listWeekName = [
|
||||
'Domingo',
|
||||
'Lunes',
|
||||
'Martes',
|
||||
'Miércoles',
|
||||
'Jueves',
|
||||
'Viernes',
|
||||
'Sábado'
|
||||
];;
|
||||
let dated = new Date();
|
||||
const dated = new Date();
|
||||
dated.setDate(dated.getDate() - 6);
|
||||
|
||||
for (let i = 0; i < 6; i++) {
|
||||
$(".listHorario").append('<label>' + listWeekName[dated.getDay()] +'</label>');
|
||||
const fragment = document.createDocumentFragment();
|
||||
const totalEl = document.querySelector(".total");
|
||||
let totalHr = 0;
|
||||
|
||||
for (let day = 0; day < weekDays.length - 1; day++) {
|
||||
const label = createElement("label", { text: weekDays[dated.getDay()] });
|
||||
fragment.append(label);
|
||||
dated.setDate(dated.getDate() + 1);
|
||||
}
|
||||
$(".listHorario").append('<label class="hoy">HOY</label>');
|
||||
$(".listHorario").append('<li class="hrTop"></li>');
|
||||
|
||||
for (let i = 0; i < timetable.length; i++) {
|
||||
fragment.append(createElement("label", { text: "HOY", classes: ["hoy"] }), createElement("li", { classes: ["hrTop"] }));
|
||||
|
||||
$(".listHorario").append(
|
||||
'<li>' +
|
||||
'<div class="time ' + ifIsEmpty(timetable[i]["6daysAgo"]) + '">' +
|
||||
'<div>' +
|
||||
'<img src="' + ifIsEmptyImage(timetable[i]["6daysAgoDirection"]) + '" />' +
|
||||
'<p>' + ifIsEmptyText(timetable[i]["6daysAgo"]) + '</p>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="time ' + ifIsEmpty(timetable[i]["5daysAgo"]) + '">' +
|
||||
'<div>' +
|
||||
'<img src="' + ifIsEmptyImage(timetable[i]["5daysAgoDirection"]) + '" />' +
|
||||
'<p>' + ifIsEmptyText(timetable[i]["5daysAgo"]) + '</p>'+
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="time ' + ifIsEmpty(timetable[i]["4daysAgo"]) + '">' +
|
||||
'<div>' +
|
||||
'<img src="' + ifIsEmptyImage(timetable[i]["4daysAgoDirection"]) + '" />' +
|
||||
'<p>' + ifIsEmptyText(timetable[i]["4daysAgo"]) + '</p>'+
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="time ' + ifIsEmpty(timetable[i]["3daysAgo"]) + '">' +
|
||||
'<div>' +
|
||||
'<img src="' + ifIsEmptyImage(timetable[i]["3daysAgoDirection"]) + '" />' +
|
||||
'<p>' + ifIsEmptyText(timetable[i]["3daysAgo"]) + '</p>'+
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="time ' + ifIsEmpty(timetable[i]["2daysAgo"]) + '">' +
|
||||
'<div>' +
|
||||
'<img src="' + ifIsEmptyImage(timetable[i]["2daysAgoDirection"]) + '" />' +
|
||||
'<p>' + ifIsEmptyText(timetable[i]["2daysAgo"]) + '</p>'+
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="time ' + ifIsEmpty(timetable[i]["1daysAgo"]) + '">' +
|
||||
'<div>' +
|
||||
'<img src="' + ifIsEmptyImage(timetable[i]["1daysAgoDirection"]) + '" />' +
|
||||
'<p>' + ifIsEmptyText(timetable[i]["1daysAgo"]) + '</p>'+
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="time ' + ifIsEmpty(timetable[i]["0daysAgo"]) + '">' +
|
||||
'<div>' +
|
||||
'<img src="' + ifIsEmptyImage(timetable[i]["0daysAgoDirection"]) + '" />' +
|
||||
'<p>' + ifIsEmptyText(timetable[i]["0daysAgo"]) + '</p>'+
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</li>'
|
||||
);
|
||||
}
|
||||
printTotalHours(timetable);
|
||||
}
|
||||
const liContent = createElement("li", {});
|
||||
const liTotals = createElement("li", {});
|
||||
timetable.forEach((row, index) => {
|
||||
for (let day = weekDays.length - 1; day >= 0; day--) {
|
||||
const img = createElement("img", { attrs: { src: ifIsEmptyImage(row[`${day}daysAgoDirection`]) } });
|
||||
const p = createElement("p", { text: row[`${day}daysAgo`] });
|
||||
|
||||
function printTotalHours(timetable) {
|
||||
if(timetable.length > 0) {
|
||||
const innerDiv = createElement("div", { childs: [img, p] });
|
||||
const outerDiv = createElement("div", { classes: ["time", ifIsEmpty(row[`${day}daysAgo`])], childs: [innerDiv] });
|
||||
liContent.append(outerDiv);
|
||||
|
||||
$(".listHorario").append('<hr>');
|
||||
$(".listHorario").append('<li><div class="time">' + secondsToHm(ifIsEmptyText(timetable[0]["6daysAgoTotal"]))
|
||||
+ ' h</div><div class="time">' + secondsToHm(ifIsEmptyText(timetable[0]["5daysAgoTotal"]))
|
||||
+ ' h</div><div class="time">' + secondsToHm(ifIsEmptyText(timetable[0]["4daysAgoTotal"]))
|
||||
+ ' h</div><div class="time">' + secondsToHm(ifIsEmptyText(timetable[0]["3daysAgoTotal"]))
|
||||
+ ' h</div><div class="time">' + secondsToHm(ifIsEmptyText(timetable[0]["2daysAgoTotal"]))
|
||||
+ ' h</div><div class="time">' + secondsToHm(ifIsEmptyText(timetable[0]["1daysAgoTotal"]))
|
||||
+ ' h</div><div class="time">' + secondsToHm(ifIsEmptyText(timetable[0]["0daysAgoTotal"]))
|
||||
+ ' h</div></li>');
|
||||
|
||||
$(".total").text('Total: ' +
|
||||
secondsToHm(
|
||||
Number(timetable[0]["6daysAgoTotal"] == null) +
|
||||
Number(timetable[0]["5daysAgoTotal"]) +
|
||||
Number(timetable[0]["4daysAgoTotal"]) +
|
||||
Number(timetable[0]["3daysAgoTotal"]) +
|
||||
Number(timetable[0]["2daysAgoTotal"]) +
|
||||
Number(timetable[0]["1daysAgoTotal"]) +
|
||||
Number(timetable[0]["0daysAgoTotal"])
|
||||
) + ' h');
|
||||
} else{
|
||||
$(".total").text('Total: 0h');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function printErrores(errores) {
|
||||
let error = '';
|
||||
for (let i = 0; i < errores.length; i++) {
|
||||
if (errores[i].error) {
|
||||
error += errores[i].error + "<br>";
|
||||
if (index === 0) {
|
||||
const div = createElement("div", { classes: ["time"], text: secondsToHm(timetable[0][`${day}daysAgoTotal`]) });
|
||||
liTotals.append(div);
|
||||
totalHr += timetable[0][`${day}daysAgoTotal`] || 0;
|
||||
}
|
||||
}
|
||||
printError(error);
|
||||
fragment.append(liContent);
|
||||
if (index === timetable.length - 1) fragment.append(createElement("hr", {}), liTotals);
|
||||
});
|
||||
|
||||
totalEl.innerText = `Total: ${totalHr ? secondsToHm(totalHr) : 0} h`;
|
||||
timetableList.append(fragment);
|
||||
}
|
||||
|
||||
function ifIsEmpty(value) {
|
||||
return value.trim() ? "show" : "hide";
|
||||
}
|
||||
const ifIsEmpty = (value) => (value?.trim() ? "show" : "hide");
|
||||
|
||||
function ifIsEmptyImage(value) {
|
||||
return value.trim() ? `img/${value}.svg` :"img/in.svg";
|
||||
}
|
||||
const ifIsEmptyImage = (value) => (value?.trim() ? `img/${value}.svg` : "img/in.svg");
|
||||
|
||||
function ifIsEmptyText(value) {
|
||||
return value.toString().trim() ? value : "00:00";
|
||||
}
|
||||
|
||||
function cerrar(){
|
||||
function close() {
|
||||
localStorage.removeItem("userData");
|
||||
setTimeout(function () {
|
||||
window.location='index.html';
|
||||
}, 200);
|
||||
setTimeout(() => (window.location = "index.html"), 200);
|
||||
}
|
||||
|
||||
function secondsToHm(seconds) {
|
||||
seconds = Number(seconds);
|
||||
seconds = +seconds;
|
||||
let hours = Math.floor(seconds / 3600);
|
||||
let minutes = Math.floor(seconds % 3600 / 60);
|
||||
return hours.toString().padStart(2, '0') + ':' + minutes.toString().padStart(2, '0');
|
||||
let minutes = Math.floor((seconds % 3600) / 60);
|
||||
return hours.toString().padStart(2, "0") + ":" + minutes.toString().padStart(2, "0");
|
||||
}
|
||||
|
||||
function createElement(tag, { classes = [], text, attrs = {}, childs = [] }) {
|
||||
const el = document.createElement(tag);
|
||||
if (classes) el.classList.add(...classes);
|
||||
if (text) el.textContent = text;
|
||||
for (const [key, value] of Object.entries(attrs)) el.setAttribute(key, value);
|
||||
if (childs) el.append(...childs);
|
||||
|
||||
return el;
|
||||
}
|
|
@ -41,11 +41,10 @@ if ("addEventListener" in document) {
|
|||
|
||||
async function login() {
|
||||
try {
|
||||
const res = await call("WorkerTimeControls/login", {
|
||||
const data = await call("WorkerTimeControls/login", {
|
||||
method: "POST",
|
||||
body: { pin },
|
||||
});
|
||||
const data = await res.json();
|
||||
localStorage.setItem("userData", JSON.stringify(data));
|
||||
window.location = "clockIn.html";
|
||||
} catch (e) {
|
||||
|
@ -55,11 +54,10 @@ async function login() {
|
|||
}
|
||||
|
||||
async function signIn(user, password, device) {
|
||||
const res = await call("vnUsers/sign-in", {
|
||||
const data = await call("vnUsers/sign-in", {
|
||||
method: "POST",
|
||||
body: { user, password },
|
||||
});
|
||||
const data = await res.json();
|
||||
localStorage.setItem("token", data.token);
|
||||
localStorage.setItem("ttl", data.ttl);
|
||||
localStorage.setItem("user", user);
|
||||
|
|
36
js/main.js
36
js/main.js
|
@ -1,44 +1,37 @@
|
|||
const renewPeriod = localStorage.getItem("renewPeriod");
|
||||
let intervalId, isCheckingToken;
|
||||
const txtConfirm = document.querySelector(".txtConfirm");
|
||||
const confirm = document.querySelector(".confirm");
|
||||
const confirmBtn = document.querySelector(".confirm");
|
||||
|
||||
function confirmReset() {
|
||||
confirm.classList.remove("confirmKO");
|
||||
confirm.style.display = "none";
|
||||
confirmBtn.classList.remove("confirmKO", "fade-in", "fade-out");
|
||||
confirmBtn.style.display = "none";
|
||||
txtConfirm.textContent = "";
|
||||
}
|
||||
|
||||
// WIP: fadeIn / fadeOut
|
||||
function printError(msg) {
|
||||
confirmReset();
|
||||
const txtConfirm = document.querySelector(".txtConfirm");
|
||||
txtConfirm.textContent = msg;
|
||||
const confirm = document.querySelector(".confirm");
|
||||
confirm.classList.add("confirmKO");
|
||||
confirm.style.display = "block";
|
||||
confirmBtn.classList.add("confirmKO");
|
||||
confirmBtn.style.display = "block";
|
||||
|
||||
$(".confirm").fadeIn(200);
|
||||
setTimeout(() => {
|
||||
$(".confirm").fadeOut(200);
|
||||
setTimeout(confirmReset, 200);
|
||||
}, 2300);
|
||||
confirmBtn.classList.add("fade-in");
|
||||
setTimeout(() => (confirmBtn.classList.add("fade-out"), confirmReset()), 2300);
|
||||
}
|
||||
|
||||
async function renewToken() {
|
||||
const res = await call("AccessTokens/renewToken", {});
|
||||
const data = await call("AccessTokens/renewToken", {});
|
||||
|
||||
const data = await res.json();
|
||||
localStorage.setItem("ttl", data.ttl);
|
||||
localStorage.setItem("created", Date.now());
|
||||
}
|
||||
|
||||
async function getTokenConfig() {
|
||||
const res = await call("AccessTokenConfigs/findOne", {
|
||||
const data = await call("AccessTokenConfigs/findOne", {
|
||||
params: { filter: { fields: ["renewInterval", "renewPeriod"] } },
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
if (!data) return;
|
||||
|
||||
localStorage.setItem("renewPeriod", data.renewPeriod);
|
||||
|
@ -61,7 +54,6 @@ async function getTokenConfig() {
|
|||
}, data.renewInterval * 1000);
|
||||
}
|
||||
|
||||
/// WIP: MUST try thru it!
|
||||
async function call(url, { method = "GET", body = {}, params = {} }) {
|
||||
const controller = new AbortController();
|
||||
const { signal } = controller;
|
||||
|
@ -85,8 +77,11 @@ async function call(url, { method = "GET", body = {}, params = {} }) {
|
|||
|
||||
try {
|
||||
const res = await fetch(`/api/${url}`, opts);
|
||||
const data = await res.json();
|
||||
|
||||
if (res.ok) return data;
|
||||
clearTimeout(timeoutId);
|
||||
return res;
|
||||
throw data.error;
|
||||
} catch (e) {
|
||||
let mensaje = "Ha ocurrido un error, consulta con informática";
|
||||
|
||||
|
@ -104,7 +99,7 @@ async function call(url, { method = "GET", body = {}, params = {} }) {
|
|||
mensaje = e.message;
|
||||
break;
|
||||
default:
|
||||
switch (e.code) {
|
||||
switch (e.statusCode) {
|
||||
case 0:
|
||||
mensaje = "Not connect: Verify Network";
|
||||
break;
|
||||
|
@ -115,10 +110,11 @@ async function call(url, { method = "GET", body = {}, params = {} }) {
|
|||
mensaje = "Error 555";
|
||||
break;
|
||||
default:
|
||||
if (e.status >= 400 && e.status < 500) mensaje = e.message;
|
||||
if (e.statusCode >= 400 && e.statusCode < 500) mensaje = e.message;
|
||||
}
|
||||
}
|
||||
printError(mensaje);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -18,8 +18,7 @@
|
|||
"dependencies": {
|
||||
"dotenv": "^16.3.1",
|
||||
"fastclick": "^1.0.6",
|
||||
"jquery": "^3.7.0",
|
||||
"sweetalert2": "11.10.1"
|
||||
"sweetalert2": "^11.6.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"express": "^4.19.2",
|
||||
|
|
Loading…
Reference in New Issue