ref: change auth setting in Login Screen
This commit is contained in:
parent
9d31ea245c
commit
12c90ff120
26 changed files with 1149 additions and 99 deletions
|
|
@ -1,26 +1,46 @@
|
||||||
"use client"
|
"use client";
|
||||||
import LoginForm from "@organisms/LoginForm"
|
import React, { useState } from "react";
|
||||||
import React from "react"
|
import { useTranslation } from "react-i18next";
|
||||||
|
import ForgotPasswordForm from "@organisms/ForgotPasswordForm";
|
||||||
|
import LoginForm from "@organisms/LoginForm";
|
||||||
|
|
||||||
const LoginPage: React.FC = () => {
|
const LoginPage: React.FC = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [showForgotPassword, setShowForgotPassword] = useState(false); // Nuevo estado para alternar entre Login y Recuperación de contraseña
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen items-center justify-center bg-gradient-to-r from-blue-900 to-blue-700">
|
<div className="flex min-h-screen items-center justify-center bg-gradient-to-r from-blue-900 to-blue-700">
|
||||||
<div className="w-full max-w-md rounded-lg bg-white p-10 shadow-lg">
|
<div className="w-full max-w-md rounded-lg bg-white p-10 shadow-lg">
|
||||||
<h2 className="mb-8 text-center text-4xl font-bold text-blue-900">Iniciar Sesión</h2>
|
{showForgotPassword ? (
|
||||||
|
<>
|
||||||
<LoginForm />
|
<h2 className="mb-8 text-center text-4xl font-bold text-blue-900">{t("lbl_reset_password")}</h2>
|
||||||
|
<ForgotPasswordForm />
|
||||||
<div className="mt-4 text-center">
|
<div className="mt-4 text-center">
|
||||||
<button
|
<button
|
||||||
onClick={() => alert("Recuperación de contraseña")} //TODO: Implement password recovery
|
onClick={() => setShowForgotPassword(false)} // Permitir volver al formulario de Login
|
||||||
className="text-sm text-blue-600 hover:underline"
|
className="text-sm text-blue-600 hover:underline"
|
||||||
>
|
>
|
||||||
¿Olvidaste tu contraseña?
|
{t("msj_back_to_login")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<h2 className="mb-8 text-center text-4xl font-bold text-blue-900">{t("lbl_login")}</h2>
|
||||||
|
<LoginForm />
|
||||||
|
<div className="mt-4 text-center">
|
||||||
|
<button
|
||||||
|
onClick={() => setShowForgotPassword(true)} // Mostrar el formulario de recuperación de contraseña
|
||||||
|
className="text-sm text-blue-600 hover:underline"
|
||||||
|
>
|
||||||
|
{t("msj_forgot_password")}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default LoginPage
|
export default LoginPage;
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
import CheckoutScreen from "@app/selfcheckout/CheckoutScreen"
|
|
||||||
import LoyaltyProgramScreen from "@app/selfcheckout/LoyaltyProgramScreen"
|
|
||||||
import PreCheckoutScreen from "@app/selfcheckout/PreCheckoutScreen"
|
|
||||||
import React from "react"
|
import React from "react"
|
||||||
|
import PreCheckoutScreen from "@app/selfcheckout/precheckout/page"
|
||||||
|
|
||||||
const HomeAfterLogin: React.FC = () => {
|
const HomeAfterLogin: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-100 p-6">
|
<div className="min-h-screen bg-gray-100 p-6">
|
||||||
<LoyaltyProgramScreen />
|
<PreCheckoutScreen />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
|
import React from "react";
|
||||||
import "@package/styles/tailwind.css";
|
import "@package/styles/tailwind.css";
|
||||||
import Navbar from "@organisms/Navbar";
|
import Navbar from "@organisms/Navbar";
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
|
|
||||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||||
|
|
|
||||||
22
app/page.tsx
22
app/page.tsx
|
|
@ -2,10 +2,13 @@
|
||||||
import Grid from "@mui/material/Grid2"
|
import Grid from "@mui/material/Grid2"
|
||||||
import Image from "next/image"
|
import Image from "next/image"
|
||||||
import { useRouter } from "next/navigation"
|
import { useRouter } from "next/navigation"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
import Button from "@package/components/atoms/Button"
|
import Button from "@package/components/atoms/Button"
|
||||||
|
|
||||||
const HomePage: React.FC = () => {
|
const HomePage: React.FC = () => {
|
||||||
const router = useRouter()
|
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
const handleLoginClick = () => {
|
const handleLoginClick = () => {
|
||||||
router.push("/account/login")
|
router.push("/account/login")
|
||||||
|
|
@ -40,23 +43,24 @@ const HomePage: React.FC = () => {
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid size={{ xs: 12, md: 6 }} textAlign={{ xs: "center", md: "left" }} padding={{ md: "0 24px" }}>
|
<Grid size={{ xs: 12, md: 6 }} textAlign={{ xs: "center", md: "left" }} padding={{ md: "0 24px" }}>
|
||||||
<h1 className="mb-4 text-5xl font-bold text-white md:text-6xl">SELF CHECKOUT</h1>
|
<h1 className="mb-4 text-5xl font-bold text-white md:text-6xl">{t('tle_self_checkout')}</h1>
|
||||||
|
|
||||||
<p className="mb-6 text-xl text-gray-200 md:text-2xl">Mejorando la experiencia en tienda</p>
|
<p className="mb-6 text-xl text-gray-200 md:text-2xl">{t('msj_improving_in_store_experience')}</p>
|
||||||
|
|
||||||
<Grid container justifyContent={{ xs: "center", md: "flex-start" }} spacing={2}>
|
<Grid container justifyContent={{ xs: "center", md: "flex-start" }} spacing={2}>
|
||||||
<Grid size={{ xs: 6, md: 6 }}>
|
<Grid size={{ xs: 12, md: 6 }}>
|
||||||
<Button
|
<Button
|
||||||
label="Iniciar Sesión"
|
label={t("lbl_login")}
|
||||||
onClick={handleLoginClick}
|
onClick={handleLoginClick}
|
||||||
className="rounded-lg bg-blue-600 px-6 py-3 text-lg text-white transition hover:bg-blue-700"
|
className="rounded-lg bg-blue-600 px-6 py-3 text-lg text-white transition hover:bg-blue-700 h-16 w-full"
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid size={{ xs: 6, md: 6 }}>
|
<Grid size={{ xs: 12, md: 6 }}>
|
||||||
<Button
|
<Button
|
||||||
label="Ver Funcionalidades"
|
label={t("lbl_features")}
|
||||||
onClick={handleFeaturesClick}
|
onClick={handleFeaturesClick}
|
||||||
className="rounded-lg bg-gray-200 px-6 py-3 text-lg text-blue-600 transition hover:bg-gray-300"
|
className="rounded-lg bg-gray-200 px-6 py-3 text-lg text-blue-600 transition hover:bg-gray-300 h-16 w-full"
|
||||||
|
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
|
||||||
1
env.mjs
1
env.mjs
|
|
@ -14,5 +14,6 @@ export const env = createEnv({
|
||||||
runtimeEnv: {
|
runtimeEnv: {
|
||||||
ANALYZE: process.env.ANALYZE,
|
ANALYZE: process.env.ANALYZE,
|
||||||
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
|
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
|
||||||
|
REACT_APP_API_KEY: process.env.REACT_APP_API_KEY,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,10 @@ const config = withPlugins([[withBundleAnalyzer({ enabled: env.ANALYZE })]], {
|
||||||
reactStrictMode: true,
|
reactStrictMode: true,
|
||||||
experimental: { instrumentationHook: false },
|
experimental: { instrumentationHook: false },
|
||||||
env: {
|
env: {
|
||||||
REACT_APP_API_URL: process.env.REACT_APP_API_URL, // Para que esté disponible en el cliente
|
REACT_APP_API_URL: process.env.REACT_APP_API_URL,
|
||||||
|
API_PASS_SERVICE: process.env.REACT_APP_API_PASS_SERVICE,
|
||||||
|
REACT_APP_API_KEY: process.env.REACT_APP_API_KEY,
|
||||||
|
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
|
||||||
},
|
},
|
||||||
|
|
||||||
rewrites() {
|
rewrites() {
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@
|
||||||
"@t3-oss/env-nextjs": "^0.7.1",
|
"@t3-oss/env-nextjs": "^0.7.1",
|
||||||
"@vercel/otel": "^1.10.0",
|
"@vercel/otel": "^1.10.0",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
|
"crypto-js": "^4.2.0",
|
||||||
"i18next": "^23.15.1",
|
"i18next": "^23.15.1",
|
||||||
"jaeger-client": "^3.19.0",
|
"jaeger-client": "^3.19.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,10 @@ interface InputProps {
|
||||||
value: string
|
value: string
|
||||||
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void
|
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void
|
||||||
autocomplete?: string
|
autocomplete?: string
|
||||||
|
disabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const Input: React.FC<InputProps> = ({ type, placeholder, value, onChange, autocomplete }) => {
|
const Input: React.FC<InputProps> = ({ type, placeholder, value, onChange, autocomplete, disabled }) => {
|
||||||
return (
|
return (
|
||||||
<TextField
|
<TextField
|
||||||
type={type}
|
type={type}
|
||||||
|
|
@ -20,6 +21,7 @@ const Input: React.FC<InputProps> = ({ type, placeholder, value, onChange, autoc
|
||||||
autoComplete={autocomplete}
|
autoComplete={autocomplete}
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,13 @@ interface LabelProps {
|
||||||
|
|
||||||
const Label: React.FC<LabelProps> = ({ htmlFor, text }) => {
|
const Label: React.FC<LabelProps> = ({ htmlFor, text }) => {
|
||||||
return (
|
return (
|
||||||
<Typography variant="subtitle1" component="label" htmlFor={htmlFor} className="mb-2 block">
|
<label htmlFor={htmlFor} className="mb-2 block">
|
||||||
{text}
|
<Typography variant="subtitle1">
|
||||||
</Typography>
|
{text}
|
||||||
|
</Typography>
|
||||||
|
</label>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Label
|
export default Label
|
||||||
|
|
||||||
|
|
|
||||||
56
package/components/organisms/ForgotPasswordForm.tsx
Normal file
56
package/components/organisms/ForgotPasswordForm.tsx
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
"use client";
|
||||||
|
import { Box, Typography } from "@mui/material";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import Button from "@atoms/Button";
|
||||||
|
import Input from "@atoms/Input";
|
||||||
|
import Label from "@atoms/Label";
|
||||||
|
import { ResetPassword } from "@package/config/api/Admin/auth/ResetPassword/page";
|
||||||
|
|
||||||
|
const ForgotPasswordForm: React.FC = () => {
|
||||||
|
const [email, setEmail] = useState("");
|
||||||
|
const [message, setMessage] = useState<string | null>(null);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const handleResetPassword = async () => {
|
||||||
|
try {
|
||||||
|
const response = await ResetPassword({ id: email });
|
||||||
|
|
||||||
|
if (response.correct) {
|
||||||
|
setMessage(t("msj_Password_reset_email_sent"));
|
||||||
|
setError(null);
|
||||||
|
} else {
|
||||||
|
setError(response.message || t("msj_Error_sending_password_reset"));
|
||||||
|
setMessage(null);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
setError(t("msj_Error_sending_password_reset"));
|
||||||
|
setMessage(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{ display: "flex", flexDirection: "column", gap: 3, width: "100%" }}>
|
||||||
|
<Typography variant="h6">{t("lbl_reset_password")}</Typography>
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Label htmlFor={t("lbl_email")} text={t("lbl_email")} />
|
||||||
|
<Input
|
||||||
|
type="email"
|
||||||
|
placeholder={t("msj_enter_your_email")}
|
||||||
|
value={email}
|
||||||
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
|
autocomplete="email"
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{message && <Typography color="primary">{message}</Typography>}
|
||||||
|
{error && <Typography color="error">{error}</Typography>}
|
||||||
|
|
||||||
|
<Button label={t("lbl_reset_password")} onClick={handleResetPassword} variant="primary" />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ForgotPasswordForm;
|
||||||
|
|
@ -1,62 +1,81 @@
|
||||||
import MoreVertIcon from "@mui/icons-material/MoreVert"
|
import MoreVertIcon from "@mui/icons-material/MoreVert";
|
||||||
import PersonIcon from "@mui/icons-material/Person"
|
import PersonIcon from "@mui/icons-material/Person";
|
||||||
import { Card, CardContent, CardHeader, IconButton, Typography } from "@mui/material"
|
import { Card, CardContent, IconButton, Typography } from "@mui/material";
|
||||||
import Grid from "@mui/material/Grid2"
|
import Grid from "@mui/material/Grid2";
|
||||||
import React from "react"
|
import React, { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
interface HeaderProps {
|
interface HeaderProps {
|
||||||
sx?: object
|
sx?: object;
|
||||||
|
onToggleButtonPanel: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Header: React.FC<HeaderProps> = ({ sx }) => {
|
const Header: React.FC<HeaderProps> = ({ sx, onToggleButtonPanel }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const [currentDateTime, setCurrentDateTime] = useState(new Date());
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
setCurrentDateTime(new Date());
|
||||||
|
}, 60000);
|
||||||
|
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const formattedDate = currentDateTime.toLocaleDateString(undefined, {
|
||||||
|
weekday: "short",
|
||||||
|
day: "numeric",
|
||||||
|
month: "short",
|
||||||
|
});
|
||||||
|
const formattedTime = currentDateTime.toLocaleTimeString(undefined, {
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card sx={{ ...sx, p: 0.1 }}>
|
<Card sx={{ ...sx, p: 0 }}>
|
||||||
<CardHeader
|
<CardContent sx={{ padding: "1px 8px" }}>
|
||||||
title="Generic userd"
|
|
||||||
subheader="Add Member"
|
|
||||||
titleTypographyProps={{ color: "text.primary", fontWeight: "bold", fontSize: "1rem" }}
|
|
||||||
subheaderTypographyProps={{ color: "text.secondary", fontSize: "0.75rem" }}
|
|
||||||
sx={{ paddingBottom: 0 }}
|
|
||||||
/>
|
|
||||||
<CardContent sx={{ paddingTop: 0.2 }}>
|
|
||||||
<Grid container alignItems="center" justifyContent="space-between">
|
<Grid container alignItems="center" justifyContent="space-between">
|
||||||
<Grid>
|
<Grid>
|
||||||
|
<Typography variant="body2" color="text.primary" sx={{ fontWeight: "bold" }}>
|
||||||
|
Generic user
|
||||||
|
</Typography>
|
||||||
<Typography variant="body2" color="text.secondary">
|
<Typography variant="body2" color="text.secondary">
|
||||||
Fri, 13 Sept
|
Add Member
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Typography variant="body2" color="text.secondary">
|
<Typography variant="body2" color="text.secondary">
|
||||||
9:36 am
|
{formattedDate} - {formattedTime}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Typography variant="body2" color="text.secondary">
|
<Typography variant="body2" color="text.secondary">
|
||||||
{t('lbl_Store')}: <strong>8004</strong>
|
{t("lbl_Store")}: <strong>8004</strong> | {t("lbl_register")}: <strong>317</strong>
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Typography variant="body2" color="text.secondary">
|
<Grid container alignItems="center">
|
||||||
{t('lbl_register')}: <strong>317</strong>
|
<PersonIcon sx={{ color: "text.secondary", fontSize: "18px", mr: 0.5 }} />
|
||||||
</Typography>
|
<Typography variant="body2" color="text.secondary">
|
||||||
</Grid>
|
{t("lbl_associate")} <strong>2</strong>
|
||||||
<Grid container alignItems="center">
|
</Typography>
|
||||||
<PersonIcon sx={{ color: "text.secondary", fontSize: "18px", mr: 0.9 }} /> {/* Icono más pequeño */}
|
</Grid>
|
||||||
<Typography variant="body2" color="text.secondary">
|
|
||||||
{t('lbl_associate')} <strong>2</strong>
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<IconButton size="small">
|
<IconButton size="small" onClick={onToggleButtonPanel}>
|
||||||
<MoreVertIcon color="action" sx={{ fontSize: "18px" }} />
|
<MoreVertIcon color="action" sx={{ fontSize: "18px" }} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default Header
|
export default Header;
|
||||||
|
|
|
||||||
|
|
@ -3,24 +3,28 @@ import { Box, Typography } from "@mui/material"
|
||||||
import { useRouter } from "next/navigation"
|
import { useRouter } from "next/navigation"
|
||||||
import React, { useState } from "react"
|
import React, { useState } from "react"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import { auth } from "@app/account/controller/AuthController"
|
|
||||||
import Button from "@atoms/Button"
|
import Button from "@atoms/Button"
|
||||||
import Input from "@atoms/Input"
|
import Input from "@atoms/Input"
|
||||||
import Label from "@atoms/Label"
|
import Label from "@atoms/Label"
|
||||||
|
import {Login} from "@package/config/api/Admin/auth/Login/page"
|
||||||
|
|
||||||
const LoginForm: React.FC = () => {
|
const LoginForm: React.FC = () => {
|
||||||
const [username, setUsername] = useState("")
|
const [email, setEmail] = useState("")
|
||||||
const [password, setPassword] = useState("")
|
const [password, setPassword] = useState("")
|
||||||
const [error, setError] = useState<string | null>(null)
|
const [error, setError] = useState<string | null>(null)
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const handleLogin = async () => {
|
const handleLogin = async () => {
|
||||||
const token = await auth(username, password)
|
try {
|
||||||
if (token) {
|
const response = await Login(email, password)
|
||||||
router.push("/home")
|
if (response.correct) {
|
||||||
} else {
|
router.push("/selfcheckout/precheckout")
|
||||||
setError( t("msj_Incorrect_credentials_try_again."))
|
} else {
|
||||||
|
setError(response.message || t("msj_Incorrect_credentials_try_again"))
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
setError(t("msj_Incorrect_credentials_try_again"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -34,13 +38,13 @@ const LoginForm: React.FC = () => {
|
||||||
sx={{ display: "flex", flexDirection: "column", gap: 3, width: "100%" }}
|
sx={{ display: "flex", flexDirection: "column", gap: 3, width: "100%" }}
|
||||||
>
|
>
|
||||||
<Box>
|
<Box>
|
||||||
<Label htmlFor={t("lbl_username")} text={t("lbl_username")} />
|
<Label htmlFor={t("lbl_email")} text={t("lbl_email")} />
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="email"
|
||||||
placeholder={t("msj_enter_your_username")}
|
placeholder={t("msj_enter_your_email")}
|
||||||
value={username}
|
value={email}
|
||||||
onChange={(e) => setUsername(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
autocomplete="username"
|
autocomplete="email"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
import LanguageIcon from '@mui/icons-material/Language';
|
||||||
import Grid from '@mui/material/Grid2';
|
import Grid from '@mui/material/Grid2';
|
||||||
|
import IconButton from '@mui/material/IconButton';
|
||||||
|
import Menu from '@mui/material/Menu';
|
||||||
import MenuItem from '@mui/material/MenuItem';
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
import Select from '@mui/material/Select';
|
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import i18n from '@package/utils/language/i18n';
|
import i18n from '@package/utils/language/i18n';
|
||||||
|
|
@ -9,10 +11,20 @@ import i18n from '@package/utils/language/i18n';
|
||||||
const Navbar: React.FC = () => {
|
const Navbar: React.FC = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [language, setLanguage] = useState(i18n.language || 'en');
|
const [language, setLanguage] = useState(i18n.language || 'en');
|
||||||
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||||
|
|
||||||
const changeLanguage = (lng: string) => {
|
const changeLanguage = (lng: string) => {
|
||||||
i18n.changeLanguage(lng);
|
i18n.changeLanguage(lng);
|
||||||
setLanguage(lng);
|
setLanguage(lng);
|
||||||
|
handleClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
|
setAnchorEl(event.currentTarget);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
setAnchorEl(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -30,15 +42,24 @@ const Navbar: React.FC = () => {
|
||||||
<h1 className="text-white font-bold">{t('lbl_wellcome')}</h1>
|
<h1 className="text-white font-bold">{t('lbl_wellcome')}</h1>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid>
|
<Grid>
|
||||||
<Select
|
{/* IconButton with globe icon to open language menu */}
|
||||||
value={language}
|
<IconButton
|
||||||
onChange={(e) => changeLanguage(e.target.value as string)}
|
aria-controls="language-menu"
|
||||||
variant="outlined"
|
aria-haspopup="true"
|
||||||
className="bg-white text-black"
|
onClick={handleClick}
|
||||||
|
className="text-white"
|
||||||
>
|
>
|
||||||
<MenuItem value="en">English</MenuItem>
|
<LanguageIcon />
|
||||||
<MenuItem value="es">Español</MenuItem>
|
</IconButton>
|
||||||
</Select>
|
<Menu
|
||||||
|
id="language-menu"
|
||||||
|
anchorEl={anchorEl}
|
||||||
|
open={Boolean(anchorEl)}
|
||||||
|
onClose={handleClose}
|
||||||
|
>
|
||||||
|
<MenuItem onClick={() => changeLanguage('en')}>English</MenuItem>
|
||||||
|
<MenuItem onClick={() => changeLanguage('es')}>Español</MenuItem>
|
||||||
|
</Menu>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1 +1,4 @@
|
||||||
export const BASE_URL_AUTHENTICATION = "/AUTHENTICATION-SERVICE/rhi-bpc/"
|
export const BASE_URL_AUTHENTICATION = "/AUTHENTICATION-SERVICE/rhi-bpc/"
|
||||||
|
export const BASE_URL_ADMINISTRATION = '/ADMINISTRATION-SERVICE/rhi-bpc/v2/'
|
||||||
|
export const BASE_URL_CUSTOMER_VALIDATION = '/CUSTOMER-SERVICE/rhi-bpc/v2/customer-type-validation-method/'
|
||||||
|
export const BASE_URL_CUSTOMER_TYPEDNI = '/CUSTOMER-SERVICE/rhi-bpc/v2/type-customer-dni/'
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,23 @@ import i18n from "@package/utils/language/i18n"
|
||||||
|
|
||||||
const BASE_URL = process.env.NEXT_PUBLIC_API_URL
|
const BASE_URL = process.env.NEXT_PUBLIC_API_URL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches data from a specified endpoint and returns a `ReturnService` object.
|
||||||
|
*
|
||||||
|
* @template T - The type of the data expected from the API response.
|
||||||
|
* @param {string} BASE_URL_MODULE - The base URL module to be appended to the base URL.
|
||||||
|
* @param {string} endpoint - The specific API endpoint to fetch data from.
|
||||||
|
* @param {RequestInit} [options={}] - Optional configuration for the fetch request.
|
||||||
|
* @returns {Promise<ReturnService<T>>} A promise that resolves to a `ReturnService` object containing the fetched data.
|
||||||
|
*
|
||||||
|
* @throws {Error} If the fetch request fails or the response is not ok.
|
||||||
|
*/
|
||||||
async function FetchData<T>(
|
async function FetchData<T>(
|
||||||
BASE_URL_MODULE: string,
|
BASE_URL_MODULE: string,
|
||||||
endpoint: string,
|
endpoint: string,
|
||||||
options: RequestInit = {}
|
options: RequestInit = {}
|
||||||
): Promise<ReturnService<T>> {
|
): Promise<ReturnService<T>> {
|
||||||
|
|
||||||
const url = `${BASE_URL}${BASE_URL_MODULE}${endpoint}`
|
const url = `${BASE_URL}${BASE_URL_MODULE}${endpoint}`
|
||||||
const text = i18n.t("errorProcessingAPI") as string
|
const text = i18n.t("errorProcessingAPI") as string
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,14 @@ export class ReturnService<T> {
|
||||||
errorCode: number
|
errorCode: number
|
||||||
object: T
|
object: T
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an instance of the ReturnService class.
|
||||||
|
*
|
||||||
|
* @param object - The object of type T to be returned.
|
||||||
|
* @param correct - A boolean indicating if the operation was correct. Defaults to true.
|
||||||
|
* @param message - A message providing additional information. Defaults to an empty string.
|
||||||
|
* @param errorCode - An error code associated with the operation. Defaults to 0.
|
||||||
|
*/
|
||||||
constructor(object: T, correct = true, message = "", errorCode = 0) {
|
constructor(object: T, correct = true, message = "", errorCode = 0) {
|
||||||
this.correct = correct
|
this.correct = correct
|
||||||
this.message = message
|
this.message = message
|
||||||
|
|
|
||||||
19
package/config/api/Admin/auth/GetToken/page.tsx
Normal file
19
package/config/api/Admin/auth/GetToken/page.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { auth } from "@package/config/api/Auth/page"
|
||||||
|
import { API_PASS_SERVICE, API_USER_TOKEN_SERVICE } from "@package/utils/Constants"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves an authentication token using the provided user token service and password service.
|
||||||
|
*
|
||||||
|
* @returns {Promise<any>} The authentication token.
|
||||||
|
* @throws {Error} If `API_USER_TOKEN_SERVICE` or `API_PASS_SERVICE` is undefined.
|
||||||
|
*/
|
||||||
|
export async function GetToken() {
|
||||||
|
try {
|
||||||
|
if (!API_USER_TOKEN_SERVICE || !API_PASS_SERVICE) {
|
||||||
|
throw new Error("API_USER_TOKEN_SERVICE or API_PASS_SERVICE is undefined");
|
||||||
|
}
|
||||||
|
return await auth(API_USER_TOKEN_SERVICE, API_PASS_SERVICE)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('error' + error)
|
||||||
|
}
|
||||||
|
}
|
||||||
68
package/config/api/Admin/auth/Login/page.tsx
Normal file
68
package/config/api/Admin/auth/Login/page.tsx
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
import { GetToken } from '@package/config/api/Admin/auth/GetToken/page';
|
||||||
|
import { BASE_URL_ADMINISTRATION } from '@package/config/ApiPath';
|
||||||
|
import { FetchData } from '@package/config/FetchData';
|
||||||
|
import { ReturnService } from '@package/config/ReturnService';
|
||||||
|
import i18n from '@package/utils/language/i18n';
|
||||||
|
import { deleteKeyApi, deleteUser, encrypt, setKeyApi, setUser } from '@package/utils/Utilities';
|
||||||
|
|
||||||
|
interface LoginResponse {
|
||||||
|
correct?: boolean;
|
||||||
|
success: boolean;
|
||||||
|
message: string;
|
||||||
|
errorCode?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authenticates a user by their username and password.
|
||||||
|
*
|
||||||
|
* @param {string} ae_user - The username of the user.
|
||||||
|
* @param {string} ae_password - The password of the user.
|
||||||
|
* @returns {Promise<LoginResponse>} A promise that resolves to a LoginResponse object indicating the success or failure of the login attempt.
|
||||||
|
*
|
||||||
|
* @throws Will return an error response if the token retrieval fails, the credentials are incorrect, or an unexpected error occurs.
|
||||||
|
*/
|
||||||
|
async function Login(ae_user: string, ae_password: string): Promise<LoginResponse> {
|
||||||
|
try {
|
||||||
|
const token = await GetToken();
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
return { success: false, message: i18n.t('errorTokenAPI'), errorCode: 102 };
|
||||||
|
}
|
||||||
|
|
||||||
|
ae_password = encrypt(ae_password);
|
||||||
|
|
||||||
|
const options: RequestInit = {
|
||||||
|
method: 'post',
|
||||||
|
mode: 'cors' as RequestMode,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: 'Bearer ' + token,
|
||||||
|
lng: i18n.language,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
email: ae_user,
|
||||||
|
password: ae_password,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const data: ReturnService<any> = await FetchData(BASE_URL_ADMINISTRATION, 'user/login', options);
|
||||||
|
|
||||||
|
if (data.correct && data.object) {
|
||||||
|
setKeyApi(token);
|
||||||
|
setUser(data.object.object);
|
||||||
|
|
||||||
|
return data.object;
|
||||||
|
} else {
|
||||||
|
deleteKeyApi();
|
||||||
|
deleteUser();
|
||||||
|
return { success: false, message: data.message || i18n.t('msj_Incorrect_credentials'), errorCode: data.errorCode };
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
deleteKeyApi();
|
||||||
|
deleteUser();
|
||||||
|
return { success: false, message: i18n.t('errorUnexpected'), errorCode: 500 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export { Login };
|
||||||
66
package/config/api/Admin/auth/ResetPassword/page.tsx
Normal file
66
package/config/api/Admin/auth/ResetPassword/page.tsx
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
import { GetToken } from "@package/config/api/Admin/auth/GetToken/page"
|
||||||
|
import { BASE_URL_ADMINISTRATION } from "@package/config/ApiPath";
|
||||||
|
import { FetchData } from "@package/config/FetchData";
|
||||||
|
import { ReturnService } from "@package/config/ReturnService";
|
||||||
|
import i18n from "@package/utils/language/i18n";
|
||||||
|
import { encrypt } from "@package/utils/Utilities";
|
||||||
|
|
||||||
|
const BASE_URL_LANGUAGE = i18n.language
|
||||||
|
|
||||||
|
interface AeOptions {
|
||||||
|
id?: string;
|
||||||
|
idSubsidiary?: number;
|
||||||
|
odlPassword?: string;
|
||||||
|
password?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the password for a user.
|
||||||
|
*
|
||||||
|
* @param {AeOptions} ae_options - The options for the reset password request.
|
||||||
|
* @returns {Promise<ReturnService>} A promise that resolves to a ReturnService object indicating the result of the operation.
|
||||||
|
*
|
||||||
|
* @throws {Error} If there is an issue with fetching the token or making the API request.
|
||||||
|
*
|
||||||
|
* The function performs the following steps:
|
||||||
|
* 1. Retrieves an authentication token using the `GetToken` function.
|
||||||
|
* 2. If the token is empty, returns a `ReturnService` object indicating an error.
|
||||||
|
* 3. Constructs the request options, including headers and body, for the API call.
|
||||||
|
* 4. Makes a POST request to the `user/resetPassword` endpoint of the administration API.
|
||||||
|
* 5. If the response indicates success, returns the response object as a `ReturnService` object.
|
||||||
|
* 6. Otherwise, returns the original response data.
|
||||||
|
*/
|
||||||
|
async function ResetPassword(ae_options: AeOptions) {
|
||||||
|
const token = await GetToken()
|
||||||
|
if (token === '') {
|
||||||
|
return new ReturnService('', false, i18n.t('errorTokenAPI'), 102)
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
method: 'post',
|
||||||
|
mode: 'cors' as RequestMode,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: 'Bearer ' + token,
|
||||||
|
lng: BASE_URL_LANGUAGE
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
id: ae_options.id ?? '',
|
||||||
|
idSubsidiary: ae_options.idSubsidiary ?? 0,
|
||||||
|
odlPassword: encrypt(ae_options.odlPassword ?? '') ?? '',
|
||||||
|
password: encrypt(ae_options.password ?? '') ?? ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await FetchData(BASE_URL_ADMINISTRATION, 'user/resetPassword', options)
|
||||||
|
|
||||||
|
let result = data;
|
||||||
|
if (data.correct) {
|
||||||
|
result = data.object as ReturnService<unknown>
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export { ResetPassword }
|
||||||
75
package/config/api/Admin/user/GetTypeUsers/page.tsx
Normal file
75
package/config/api/Admin/user/GetTypeUsers/page.tsx
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
import { BASE_URL_ADMINISTRATION } from "@package/config/ApiPath"
|
||||||
|
import { FetchData } from "@package/config/FetchData"
|
||||||
|
import { ReturnService } from "@package/config/ReturnService"
|
||||||
|
import { GRID_SIZE } from "@package/utils/Constants"
|
||||||
|
import i18n from "@package/utils/language/i18n"
|
||||||
|
import { getKeyApi } from "@package/utils/Utilities"
|
||||||
|
|
||||||
|
const BASE_URL_LANGUAGE = i18n.language
|
||||||
|
|
||||||
|
interface AeObject {
|
||||||
|
id?: string;
|
||||||
|
name?: string;
|
||||||
|
idCompany?: string;
|
||||||
|
idSubsidiary?: number;
|
||||||
|
isActive?: boolean;
|
||||||
|
size?: number;
|
||||||
|
page?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches a list of user types from the administration API.
|
||||||
|
*
|
||||||
|
* @param {AeObject} ae_object - The object containing parameters for the request.
|
||||||
|
* @param {string} ae_object.id - The ID of the user type (optional).
|
||||||
|
* @param {string} ae_object.name - The name of the user type (optional).
|
||||||
|
* @param {string} ae_object.idCompany - The ID of the company (optional).
|
||||||
|
* @param {string} ae_object.idSubsidiary - The ID of the subsidiary (optional).
|
||||||
|
* @param {boolean} ae_object.isActive - The active status of the user type (optional, defaults to true).
|
||||||
|
* @param {number} ae_object.size - The number of items per page (optional, defaults to GRID_SIZE).
|
||||||
|
* @param {number} ae_object.page - The page number (optional, defaults to 0).
|
||||||
|
* @returns {Promise<ReturnService>} A promise that resolves to a ReturnService object containing the result of the API call.
|
||||||
|
*/
|
||||||
|
async function GetTypeUsers(ae_object: AeObject): Promise<ReturnService<any>> {
|
||||||
|
const token = getKeyApi()
|
||||||
|
if (token === '') {
|
||||||
|
return new ReturnService('', false, i18n.t('errorTokenAPI'), 102)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ae_object.size == null || ae_object.size <= 0) {
|
||||||
|
ae_object.size = GRID_SIZE
|
||||||
|
}
|
||||||
|
if (ae_object.page == null || ae_object.page <= 0) {
|
||||||
|
ae_object.page = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
method: 'post',
|
||||||
|
mode: 'cors' as RequestMode,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: 'Bearer ' + token,
|
||||||
|
lng: BASE_URL_LANGUAGE
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
id: ae_object.id ?? '',
|
||||||
|
name: ae_object.name ?? '',
|
||||||
|
idCompany: ae_object.idCompany ?? '',
|
||||||
|
idSubsidiary: ae_object.idSubsidiary ?? 0,
|
||||||
|
isActive: ae_object.isActive ?? true,
|
||||||
|
size: ae_object.size,
|
||||||
|
page: ae_object.page
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await FetchData(BASE_URL_ADMINISTRATION, 'type-user/all', options)
|
||||||
|
|
||||||
|
let result = data;
|
||||||
|
if (data.correct) {
|
||||||
|
result = data.object as ReturnService<unknown>
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
export { GetTypeUsers }
|
||||||
78
package/config/api/Admin/user/GetUsers/page.tsx
Normal file
78
package/config/api/Admin/user/GetUsers/page.tsx
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
import { BASE_URL_ADMINISTRATION } from "@package/config/ApiPath";
|
||||||
|
import { FetchData } from "@package/config/FetchData";
|
||||||
|
import { GRID_SIZE } from "@package/utils/Constants";
|
||||||
|
import i18n from "@package/utils/language/i18n";
|
||||||
|
import { getKeyApi } from "@package/utils/Utilities";
|
||||||
|
|
||||||
|
interface AeObject {
|
||||||
|
size: number | null;
|
||||||
|
page: number | null;
|
||||||
|
type?: string;
|
||||||
|
idSubsidiary?: number;
|
||||||
|
idCompany?: number;
|
||||||
|
name?: string;
|
||||||
|
email?: string;
|
||||||
|
isActive?: boolean | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GetUsersResponse {
|
||||||
|
correct: boolean;
|
||||||
|
message: string;
|
||||||
|
errorCode: number;
|
||||||
|
object?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function GetUsers(ae_object: AeObject): Promise<GetUsersResponse> {
|
||||||
|
const token = getKeyApi();
|
||||||
|
if (!token) {
|
||||||
|
return { correct: false, message: i18n.t('errorTokenAPI'), errorCode: 102, object: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ae_object.size === null || ae_object.size <= 0) {
|
||||||
|
ae_object.size = GRID_SIZE;
|
||||||
|
}
|
||||||
|
if (ae_object.page === null || ae_object.page <= 0) {
|
||||||
|
ae_object.page = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
method: 'post',
|
||||||
|
mode: 'cors' as RequestMode,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: 'Bearer ' + token,
|
||||||
|
lng: i18n.language,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
idType: ae_object.type ?? '',
|
||||||
|
idSubsidiary: ae_object.idSubsidiary ?? 0,
|
||||||
|
idCompany: ae_object.idCompany ?? 0,
|
||||||
|
name: ae_object.name ?? '',
|
||||||
|
mail: ae_object.email ?? '',
|
||||||
|
isActive: ae_object.isActive ?? null,
|
||||||
|
size: ae_object.size,
|
||||||
|
page: ae_object.page,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Llamada a FetchData
|
||||||
|
const response = await FetchData<any>(BASE_URL_ADMINISTRATION, 'user/all', options);
|
||||||
|
|
||||||
|
if (response.correct) {
|
||||||
|
return {
|
||||||
|
correct: true,
|
||||||
|
message: "OK",
|
||||||
|
errorCode: 0,
|
||||||
|
object: response.object,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
correct: false,
|
||||||
|
message: response.message,
|
||||||
|
errorCode: response.errorCode,
|
||||||
|
object: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { GetUsers };
|
||||||
72
package/config/api/Admin/user/SaveUser/page.tsx
Normal file
72
package/config/api/Admin/user/SaveUser/page.tsx
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
import { BASE_URL_ADMINISTRATION } from "@package/config/ApiPath";
|
||||||
|
import { FetchData } from "@package/config/FetchData";
|
||||||
|
import { ReturnService } from "@package/config/ReturnService";
|
||||||
|
import { DEFAULT_PASSWORD } from "@package/utils/Constants";
|
||||||
|
import i18n from "@package/utils/language/i18n";
|
||||||
|
import { encrypt, getKeyApi } from "@package/utils/Utilities";
|
||||||
|
|
||||||
|
const BASE_URL_LANGUAGE = i18n.language
|
||||||
|
|
||||||
|
interface UserObject {
|
||||||
|
id?: string;
|
||||||
|
idSubsidiary?: number;
|
||||||
|
idCompany?: number;
|
||||||
|
name?: string;
|
||||||
|
lastName?: string;
|
||||||
|
email?: string;
|
||||||
|
cellphone?: string;
|
||||||
|
password?: string;
|
||||||
|
type?: string;
|
||||||
|
isActive?: boolean;
|
||||||
|
isAdmin?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function SaveUser(ae_object: UserObject) {
|
||||||
|
const token = getKeyApi()
|
||||||
|
if (token === '') {
|
||||||
|
return new ReturnService('', false, i18n.t('errorTokenAPI'), 102)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ae_object.id ?? '').trim().length <= 0 && (ae_object.password ?? '').trim().length <= 0) {
|
||||||
|
ae_object.password = DEFAULT_PASSWORD
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ae_object.password ?? '').trim().length > 0) {
|
||||||
|
ae_object.password = encrypt(ae_object.password ?? '')
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
method: 'post',
|
||||||
|
mode: 'cors' as RequestMode,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: 'Bearer ' + token,
|
||||||
|
lng: BASE_URL_LANGUAGE
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
id: (ae_object.id ?? '').trim(),
|
||||||
|
idSubsidiary: ae_object.idSubsidiary ?? 0,
|
||||||
|
idCompany: ae_object.idCompany ?? 0,
|
||||||
|
name: ae_object.name ?? '',
|
||||||
|
lastName: ae_object.lastName ?? '',
|
||||||
|
email: ae_object.email ?? '',
|
||||||
|
cellphone: ae_object.cellphone ?? '',
|
||||||
|
password: ae_object.password ?? '',
|
||||||
|
idTypeUser: ae_object.type ?? '',
|
||||||
|
isActive: ae_object.isActive ?? false,
|
||||||
|
isAdmin: ae_object.isAdmin ?? false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await FetchData(BASE_URL_ADMINISTRATION, 'user/save', options)
|
||||||
|
|
||||||
|
let result = data;
|
||||||
|
if (data.correct) {
|
||||||
|
result = data.object as ReturnService<unknown>
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export { SaveUser }
|
||||||
33
package/config/api/Auth/page.tsx
Normal file
33
package/config/api/Auth/page.tsx
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { BASE_URL_AUTHENTICATION } from "@package/config/ApiPath"
|
||||||
|
import { FetchData } from "@package/config/FetchData"
|
||||||
|
|
||||||
|
async function auth(ae_user: string, ae_password: string): Promise<string> {
|
||||||
|
const options: RequestInit = {
|
||||||
|
method: "POST",
|
||||||
|
mode: 'cors',
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
username: ae_user,
|
||||||
|
password: ae_password,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await FetchData<{ token: string }>(BASE_URL_AUTHENTICATION, "login", options)
|
||||||
|
|
||||||
|
console.log(data)
|
||||||
|
if (data.correct) {
|
||||||
|
if (data.object) {
|
||||||
|
return data.object.token
|
||||||
|
} else {
|
||||||
|
console.error("Error: data.object is null or undefined")
|
||||||
|
return "error en la autenticación"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error("Error en la autenticación:", data.message)
|
||||||
|
return "error en la autenticación, mensaje: " + data.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { auth }
|
||||||
18
package/utils/Constants.tsx
Normal file
18
package/utils/Constants.tsx
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import i18n from "./language/i18n"
|
||||||
|
|
||||||
|
export const API_PASS_SERVICE = process.env.API_PASS_SERVICE
|
||||||
|
|
||||||
|
export const API_USER_TOKEN_SERVICE = 'admin'
|
||||||
|
|
||||||
|
export const SECRET_KEY = 'ADAERYUUCZ1PICANR'
|
||||||
|
|
||||||
|
export const GRID_SIZE = 10
|
||||||
|
|
||||||
|
export const DEFAULT_PASSWORD = 'Bopos123'
|
||||||
|
|
||||||
|
// Constants for special cases in Dashboard statistics
|
||||||
|
export const INFINITY_SYMBOL = '∞'
|
||||||
|
|
||||||
|
export const NOT_AVAILABLE = 'N/A'
|
||||||
|
|
||||||
|
export const BASE_URL_LANGUAGE = i18n.language
|
||||||
402
package/utils/Utilities.tsx
Normal file
402
package/utils/Utilities.tsx
Normal file
|
|
@ -0,0 +1,402 @@
|
||||||
|
// import crypto from 'crypto';
|
||||||
|
import crypto from 'crypto';
|
||||||
|
import { INFINITY_SYMBOL, NOT_AVAILABLE } from '@package/utils/Constants';
|
||||||
|
|
||||||
|
const password: string = process.env.REACT_APP_API_KEY || '';
|
||||||
|
|
||||||
|
const startForString = '420';
|
||||||
|
const endForString = '021';
|
||||||
|
|
||||||
|
const keyApiStore = '3a1f8a790c238b8df0d447b53d792134';
|
||||||
|
const keyUser = '2a5f8a790c238u8sf0e447r53d344378';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypts a given object using AES-256-CBC encryption.
|
||||||
|
*
|
||||||
|
* @param object - The object to be encrypted. If the object is null, an empty string is returned.
|
||||||
|
* @returns The encrypted string representation of the object.
|
||||||
|
*/
|
||||||
|
const encryptObject = (object: any): string => {
|
||||||
|
if (object === null) return '';
|
||||||
|
const jsonString = JSON.stringify(object);
|
||||||
|
const cipher = crypto.createCipher('aes-256-cbc', password);
|
||||||
|
let encryptedString = cipher.update(jsonString, 'utf-8', 'hex');
|
||||||
|
encryptedString += cipher.final('hex');
|
||||||
|
|
||||||
|
return encryptedString;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts an encrypted string and parses it into an object.
|
||||||
|
*
|
||||||
|
* @param {string | null} encryptedString - The encrypted string to decrypt. If null, returns an empty string.
|
||||||
|
* @returns {any} - The decrypted and parsed object.
|
||||||
|
*/
|
||||||
|
const decryptObject = (encryptedString: string | null): any => {
|
||||||
|
if (encryptedString === null) return '';
|
||||||
|
|
||||||
|
const decipher = crypto.createDecipher('aes-256-cbc', password);
|
||||||
|
let decryptedString = decipher.update(encryptedString, 'hex', 'utf-8');
|
||||||
|
decryptedString += decipher.final('utf-8');
|
||||||
|
|
||||||
|
return JSON.parse(decryptedString);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypts the given data using AES-256-CBC encryption algorithm.
|
||||||
|
*
|
||||||
|
* @param {string | null} data - The data to be encrypted. If null, an empty string is returned.
|
||||||
|
* @returns {string} The encrypted data in hexadecimal format, concatenated with the initialization vector (IV).
|
||||||
|
*
|
||||||
|
* @throws Will log an error to the console if encryption fails and return an empty string.
|
||||||
|
*/
|
||||||
|
function encrypt(data: string | null): string {
|
||||||
|
try{
|
||||||
|
if (data === null) return '';
|
||||||
|
|
||||||
|
const passwordValid = password.slice(4).slice(0, -4);
|
||||||
|
const iv = crypto.randomBytes(16);
|
||||||
|
const cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(passwordValid, 'hex'), iv);
|
||||||
|
let encrypted = cipher.update(data, 'utf-8', 'hex');
|
||||||
|
encrypted += cipher.final('hex');
|
||||||
|
|
||||||
|
return encrypted + '.' + iv.toString('hex');
|
||||||
|
}
|
||||||
|
catch(e){
|
||||||
|
console.error("Error encriptando: ", e);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Concatenates the given string with predefined start and end strings.
|
||||||
|
*
|
||||||
|
* @param data - The string to be concatenated.
|
||||||
|
* @returns The concatenated string with start and end strings.
|
||||||
|
*/
|
||||||
|
function setInitEndString(data: string): string {
|
||||||
|
return startForString + data + endForString;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a string representing the current date in the format DDMMYYYY.
|
||||||
|
*
|
||||||
|
* @returns {string} The current date formatted as a string.
|
||||||
|
*/
|
||||||
|
function getDateSaveStore(): string {
|
||||||
|
const date = new Date();
|
||||||
|
const dia = date.getDate();
|
||||||
|
const mes = date.getMonth() + 1;
|
||||||
|
const año = date.getFullYear();
|
||||||
|
|
||||||
|
return `${dia}${mes}${año}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a key-value pair in the local storage with a timestamped key.
|
||||||
|
*
|
||||||
|
* @param key - The key under which the data should be stored.
|
||||||
|
* @param data - The data to be stored.
|
||||||
|
* @throws Will log an error to the console if the storage operation fails.
|
||||||
|
*/
|
||||||
|
function setKeyStore(key: string, data: string): void {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(`${getDateSaveStore()}${key}`, data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error setting key store', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a value from localStorage based on a provided key.
|
||||||
|
*
|
||||||
|
* The key is prefixed with the current date obtained from `getDateSaveStore()`.
|
||||||
|
* If an error occurs during retrieval, it logs the error to the console and returns null.
|
||||||
|
*
|
||||||
|
* @param {string} key - The key to retrieve the value for.
|
||||||
|
* @returns {string | null} - The value associated with the key, or null if an error occurs.
|
||||||
|
*/
|
||||||
|
function getKeyStore(key: string): string | null {
|
||||||
|
try {
|
||||||
|
return localStorage.getItem(`${getDateSaveStore()}${key}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error getting key store', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a key from the local storage.
|
||||||
|
*
|
||||||
|
* @param {string} key - The key to be deleted from the local storage.
|
||||||
|
* @throws Will log an error to the console if the deletion fails.
|
||||||
|
*/
|
||||||
|
function deleteKeyStore(key: string): void {
|
||||||
|
try {
|
||||||
|
localStorage.removeItem(`${getDateSaveStore()}${key}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting key store', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the API key in the key store.
|
||||||
|
*
|
||||||
|
* @param data - The API key to be set.
|
||||||
|
* @throws Will log an error message if setting the API key fails.
|
||||||
|
*/
|
||||||
|
function setKeyApi(data: string): void {
|
||||||
|
try {
|
||||||
|
setKeyStore(keyApiStore, data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error setting API key', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the API key from the key store.
|
||||||
|
*
|
||||||
|
* @returns {string | null} The API key if it exists, otherwise `null`.
|
||||||
|
* @throws Will log an error to the console if there is an issue retrieving the key.
|
||||||
|
*/
|
||||||
|
function getKeyApi(): string | null {
|
||||||
|
try {
|
||||||
|
return getKeyStore(keyApiStore);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error getting API key', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the API key from the store.
|
||||||
|
*
|
||||||
|
* This function attempts to delete the API key from the key store.
|
||||||
|
* If an error occurs during the deletion process, it logs the error
|
||||||
|
* to the console with a message indicating the failure.
|
||||||
|
*
|
||||||
|
* @throws Will log an error message to the console if the deletion fails.
|
||||||
|
*/
|
||||||
|
function deleteKeyApi(): void {
|
||||||
|
try {
|
||||||
|
deleteKeyStore(keyApiStore);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting API key', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// User
|
||||||
|
/**
|
||||||
|
* Sets the user data in the key store after encrypting it.
|
||||||
|
*
|
||||||
|
* @param data - The user data to be stored. This can be of any type.
|
||||||
|
* @returns void
|
||||||
|
*/
|
||||||
|
function setUser(data: any): void {
|
||||||
|
setKeyStore(keyUser, encryptObject(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the user information by decrypting the stored user data.
|
||||||
|
*
|
||||||
|
* @returns {any} The decrypted user object.
|
||||||
|
*/
|
||||||
|
function getUser(): any {
|
||||||
|
return decryptObject(getKeyStore(keyUser));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the user by removing the associated key from the key store.
|
||||||
|
*
|
||||||
|
* This function calls `deleteKeyStore` with `keyUser` to remove the user's key.
|
||||||
|
*
|
||||||
|
* @function
|
||||||
|
* @returns {void} This function does not return a value.
|
||||||
|
*/
|
||||||
|
function deleteUser(): void {
|
||||||
|
deleteKeyStore(keyUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the user privileges by encrypting the provided data and storing it in the key store.
|
||||||
|
*
|
||||||
|
* @param data - The user privileges data to be encrypted and stored.
|
||||||
|
* @returns void
|
||||||
|
*/
|
||||||
|
function setUserPrivileges(data: any): void {
|
||||||
|
setKeyStore('userPrivileges', encryptObject(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the user privileges from the key store and decrypts them.
|
||||||
|
*
|
||||||
|
* @returns {any} The decrypted user privileges.
|
||||||
|
*/
|
||||||
|
function getUserPrivileges(): any {
|
||||||
|
const userPrivileges = decryptObject(getKeyStore('userPrivileges'));
|
||||||
|
return userPrivileges;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the user privileges from the key store.
|
||||||
|
*
|
||||||
|
* This function removes the 'userPrivileges' entry from the key store,
|
||||||
|
* effectively revoking any stored user privileges.
|
||||||
|
*
|
||||||
|
* @returns {void} This function does not return a value.
|
||||||
|
*/
|
||||||
|
function deleteUserPrivileges(): void {
|
||||||
|
deleteKeyStore('userPrivileges');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Page Access, Update, Create Allowed validation
|
||||||
|
/**
|
||||||
|
* Checks if the user has a specific privilege for a given page.
|
||||||
|
*
|
||||||
|
* @param {string} npage - The name of the page to check privileges for.
|
||||||
|
* @param {string} privilegeType - The type of privilege to check (e.g., 'read', 'write').
|
||||||
|
* @returns {boolean} - Returns `true` if the user has the specified privilege for the page, otherwise `false`.
|
||||||
|
*
|
||||||
|
* @throws Will log an error to the console if there is an issue checking the user's privileges.
|
||||||
|
*/
|
||||||
|
function checkUserPrivilege(npage: string, privilegeType: string): boolean {
|
||||||
|
try {
|
||||||
|
const userPrivileges = getUserPrivileges();
|
||||||
|
if (userPrivileges) {
|
||||||
|
const page = userPrivileges.find((item: any) => item.npage === npage);
|
||||||
|
if (page && page.hasOwnProperty(privilegeType)) {
|
||||||
|
return !!page[privilegeType];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error checking user privilege:', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if access to a specific page is allowed based on user privileges.
|
||||||
|
*
|
||||||
|
* @param {string} npage - The name of the page to check access for.
|
||||||
|
* @returns {boolean} - Returns `true` if the user has the required privileges and the page is active, otherwise `false`.
|
||||||
|
*/
|
||||||
|
function pageAccessAllowed(npage: string): boolean {
|
||||||
|
return checkUserPrivilege(npage, 'isActive');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the user has the privilege to update the specified page.
|
||||||
|
*
|
||||||
|
* @param {string} npage - The name of the page to check for update privileges.
|
||||||
|
* @returns {boolean} - Returns `true` if the user has update privileges for the page, otherwise `false`.
|
||||||
|
*/
|
||||||
|
function pageUpdateAllowed(npage: string): boolean {
|
||||||
|
return checkUserPrivilege(npage, 'isUpdate');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the user has the privilege to create a new page.
|
||||||
|
*
|
||||||
|
* @param {string} npage - The name of the page to check.
|
||||||
|
* @returns {boolean} - Returns `true` if the user has the 'isCreate' privilege for the specified page, otherwise `false`.
|
||||||
|
*/
|
||||||
|
function pageCreateAllowed(npage: string): boolean {
|
||||||
|
return checkUserPrivilege(npage, 'isCreate');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the percentage change between the current value and the last value.
|
||||||
|
*
|
||||||
|
* @param currentValue - The current value to compare.
|
||||||
|
* @param lastValue - The previous value to compare against.
|
||||||
|
* @returns A string representing infinity if the last value is zero and the current value is not zero,
|
||||||
|
* a string representing not available if both values are zero,
|
||||||
|
* or a number representing the percentage change rounded to two decimal places.
|
||||||
|
*/
|
||||||
|
function getPercentageChange(currentValue: number, lastValue: number): string | number {
|
||||||
|
return lastValue === 0 && currentValue !== 0
|
||||||
|
? INFINITY_SYMBOL
|
||||||
|
: lastValue === 0 && currentValue === 0
|
||||||
|
? NOT_AVAILABLE
|
||||||
|
: parseFloat((((currentValue - lastValue) / lastValue) * 100).toFixed(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the input event for a name input field.
|
||||||
|
*
|
||||||
|
* This function ensures that only alphabetic characters and spaces are allowed
|
||||||
|
* in the input field by replacing any non-alphabetic characters with an empty string.
|
||||||
|
*
|
||||||
|
* @param event - The input change event triggered by the user.
|
||||||
|
*/
|
||||||
|
const handleNameInput = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
|
event.target.value = event.target.value.replace(/[^a-zA-Z\s]/g, '');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles non-numeric input in an HTML input element.
|
||||||
|
*
|
||||||
|
* This function listens for changes in the input field and replaces any non-alphabetic characters
|
||||||
|
* with an empty string, effectively removing them. It allows only alphabetic characters and spaces.
|
||||||
|
*
|
||||||
|
* @param event - The change event triggered by the input element.
|
||||||
|
*/
|
||||||
|
const handleNonNumericInput = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
|
event.target.value = event.target.value.replace(/[^a-zA-Z\s]/g, '');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Truncates a given text to a specified length and appends ellipsis if the text exceeds that length.
|
||||||
|
*
|
||||||
|
* @param text - The text to be truncated.
|
||||||
|
* @param length - The maximum length of the truncated text.
|
||||||
|
* @returns The truncated text with ellipsis if it exceeds the specified length.
|
||||||
|
*/
|
||||||
|
const truncateText = (text: string, length: number): string => {
|
||||||
|
return text?.length > length ? `${text.substring(0, length)}...` : text;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Concatenates the names of the selected items into a single string, separated by commas.
|
||||||
|
*
|
||||||
|
* @param selectedItems - An array of objects, each containing a `name` property.
|
||||||
|
* @returns A string containing the concatenated names of the selected items.
|
||||||
|
*/
|
||||||
|
const renderName = (selectedItems: { name: string }[]): string => {
|
||||||
|
let name = '';
|
||||||
|
selectedItems?.forEach((item, index) => {
|
||||||
|
if (index === 0) {
|
||||||
|
name = item?.name;
|
||||||
|
} else {
|
||||||
|
name += ', ' + item?.name;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return name;
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
encrypt,
|
||||||
|
setInitEndString,
|
||||||
|
setKeyStore,
|
||||||
|
getKeyStore,
|
||||||
|
deleteKeyStore,
|
||||||
|
setKeyApi,
|
||||||
|
getKeyApi,
|
||||||
|
deleteKeyApi,
|
||||||
|
setUser,
|
||||||
|
getUser,
|
||||||
|
deleteUser,
|
||||||
|
setUserPrivileges,
|
||||||
|
getUserPrivileges,
|
||||||
|
deleteUserPrivileges,
|
||||||
|
pageAccessAllowed,
|
||||||
|
pageUpdateAllowed,
|
||||||
|
pageCreateAllowed,
|
||||||
|
getPercentageChange,
|
||||||
|
handleNameInput,
|
||||||
|
handleNonNumericInput,
|
||||||
|
truncateText,
|
||||||
|
renderName
|
||||||
|
};
|
||||||
|
|
@ -11,6 +11,8 @@ i18n
|
||||||
translation: {
|
translation: {
|
||||||
lbl_wellcome: "Welcome",
|
lbl_wellcome: "Welcome",
|
||||||
lbl_login: "Login",
|
lbl_login: "Login",
|
||||||
|
lbl_email: "Email",
|
||||||
|
lbl_reset_password: "Reset password",
|
||||||
lbl_username: "Username",
|
lbl_username: "Username",
|
||||||
lbl_password: "Password",
|
lbl_password: "Password",
|
||||||
lbl_enter_PLU: "Enter PLU",
|
lbl_enter_PLU: "Enter PLU",
|
||||||
|
|
@ -20,15 +22,28 @@ i18n
|
||||||
lbl_coupons : "Coupons",
|
lbl_coupons : "Coupons",
|
||||||
lbl_discount_percentage: "Discount percentage",
|
lbl_discount_percentage: "Discount percentage",
|
||||||
lbl_amount_discount: "Amount discount",
|
lbl_amount_discount: "Amount discount",
|
||||||
|
lbl_bag_fee: "Bag fee",
|
||||||
lbl_bag_credit: "Bag credit",
|
lbl_bag_credit: "Bag credit",
|
||||||
lbl_bottle_refund: "Bottle refund",
|
lbl_bottle_refund: "Bottle refund",
|
||||||
lbl_tax_exempt: "Tax exempt",
|
lbl_tax_exempt: "Tax exempt",
|
||||||
|
lbl_clear_exempt: "Clear exempt",
|
||||||
lbl_item_inquiry: "Item Inquiry",
|
lbl_item_inquiry: "Item Inquiry",
|
||||||
lbl_Store: "Store",
|
lbl_Store: "Store",
|
||||||
lbl_register: "Register",
|
lbl_register: "Register",
|
||||||
lbl_associate: "Associate",
|
lbl_associate: "Associate",
|
||||||
|
lb_open: "Open",
|
||||||
|
lbl_start: "Start",
|
||||||
|
lbl_products_list: "Products list",
|
||||||
|
lbl_information_required: "Information required",
|
||||||
|
lbl_yes: "Yes",
|
||||||
|
lbl_no: "No",
|
||||||
|
lbl_features: "Features",
|
||||||
|
lbl_delete: "Delete",
|
||||||
|
lbl_clear: "Clear",
|
||||||
|
lbl_cancel: "Cancel",
|
||||||
|
lbl_enter: "Enter",
|
||||||
|
lbl_alphabetical: "Alphabetical",
|
||||||
|
lbl_numeric: "Numeric",
|
||||||
|
|
||||||
msj_resume_previously_suspended_order: "Resume a previously suspended order",
|
msj_resume_previously_suspended_order: "Resume a previously suspended order",
|
||||||
msj_transfer_Order_another_station: "Transfer an order to another station",
|
msj_transfer_Order_another_station: "Transfer an order to another station",
|
||||||
|
|
@ -38,13 +53,30 @@ i18n
|
||||||
msj_manage_coupons: "Manage coupons",
|
msj_manage_coupons: "Manage coupons",
|
||||||
msj_manage_discount_percentage: "Manage discount percentage",
|
msj_manage_discount_percentage: "Manage discount percentage",
|
||||||
msj_manage_amount_discount: "Manage amount discount",
|
msj_manage_amount_discount: "Manage amount discount",
|
||||||
|
msj_bag_fee: "Bag fee",
|
||||||
msj_bag_credit_management: "Credit bag management",
|
msj_bag_credit_management: "Credit bag management",
|
||||||
msj_bottle_refund_management: "Bottle refund management",
|
msj_bottle_refund_management: "Bottle refund management",
|
||||||
msj_tax_exemption_management: "Tax exemption management",
|
msj_tax_exemption_management: "Tax exemption management",
|
||||||
|
msj_delete_tax_exception: "Delete tax exception",
|
||||||
msj_query_search_article: "Query or search for article",
|
msj_query_search_article: "Query or search for article",
|
||||||
msj_enter_your_username: "Enter your username",
|
msj_enter_your_username: "Enter your username",
|
||||||
msj_enter_your_password: "Enter your password",
|
msj_enter_your_password: "Enter your password",
|
||||||
msj_Incorrect_credentials_try_again: "Incorrect credentials, try again",
|
msj_Incorrect_credentials_try_again: "Incorrect credentials, try again",
|
||||||
|
msj_forgot_password: "Forgot your password?",
|
||||||
|
msj_Password_reset_email_sent: "Password reset email sent",
|
||||||
|
msj_Error_sending_password_reset: "Error sending password reset",
|
||||||
|
msj_back_to_login: "Back to login",
|
||||||
|
msj_this_box_is: "This box is",
|
||||||
|
msj_press_any_key_button_start: "Press any key or the button to start",
|
||||||
|
msj_are_you_registered_as_affiliate: "Are you registered as an affiliate?",
|
||||||
|
msj_improving_in_store_experience: "Improving in-store experience",
|
||||||
|
msj_select_type_identity_document: "Select the type of identity document",
|
||||||
|
msj_enter_number_of: "Enter the number of",
|
||||||
|
msj_enter_your_email: "Enter your email",
|
||||||
|
msj_enter_document_number: "Enter the document number",
|
||||||
|
msj_change_to: "Change to",
|
||||||
|
|
||||||
|
tle_self_checkout: "SELF CHECKOUT",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
es: {
|
es: {
|
||||||
|
|
@ -52,6 +84,8 @@ i18n
|
||||||
lbl_wellcome: "Bienvenido",
|
lbl_wellcome: "Bienvenido",
|
||||||
lbl_login: "Iniciar sesión",
|
lbl_login: "Iniciar sesión",
|
||||||
lbl_username: "Nombre de usuario",
|
lbl_username: "Nombre de usuario",
|
||||||
|
lbl_email: "Correo electrónico",
|
||||||
|
lbl_reset_password: "Restablecer contraseña",
|
||||||
lbl_password: "Contraseña",
|
lbl_password: "Contraseña",
|
||||||
lbl_enter_PLU: "Ingrese PLU",
|
lbl_enter_PLU: "Ingrese PLU",
|
||||||
lbl_transfer_order: "Transferir orden",
|
lbl_transfer_order: "Transferir orden",
|
||||||
|
|
@ -60,14 +94,28 @@ i18n
|
||||||
lbl_coupons : "Cupones",
|
lbl_coupons : "Cupones",
|
||||||
lbl_discount_percentage: "Porcentaje de descuento",
|
lbl_discount_percentage: "Porcentaje de descuento",
|
||||||
lbl_amount_discount: "Importe de descuento",
|
lbl_amount_discount: "Importe de descuento",
|
||||||
|
lbl_bag_fee: "Tarifa de bolsa",
|
||||||
lbl_bag_credit: "Bolsa de crédito",
|
lbl_bag_credit: "Bolsa de crédito",
|
||||||
lbl_bottle_refund: "Devolución de botellas",
|
lbl_bottle_refund: "Devolución de botellas",
|
||||||
lbl_tax_exempt: "Exento de impuestos",
|
lbl_tax_exempt: "Exento de impuestos",
|
||||||
|
lbl_clear_exempt: "Exento de impuestos",
|
||||||
lbl_item_inquiry: "Consulta de artículo",
|
lbl_item_inquiry: "Consulta de artículo",
|
||||||
lbl_Store: "Tienda",
|
lbl_Store: "Tienda",
|
||||||
lbl_register: "Registro",
|
lbl_register: "Registro",
|
||||||
lbl_associate: "Asociado",
|
lbl_associate: "Asociado",
|
||||||
|
lb_open: "Abierta",
|
||||||
|
lbl_start: "Comenzar",
|
||||||
|
lbl_products_list: "Lista de productos",
|
||||||
|
lbl_information_required: "Información requerida",
|
||||||
|
lbl_yes: "Sí",
|
||||||
|
lbl_no: "No",
|
||||||
|
lbl_features: "Funcionalidades",
|
||||||
|
lbl_delete: "Borrar",
|
||||||
|
lbl_clear: "Limpiar",
|
||||||
|
lbl_cancel: "Cancelar",
|
||||||
|
lbl_enter: "Entrar",
|
||||||
|
lbl_alphabetical: "Alfabético",
|
||||||
|
lbl_numeric: "Numérico",
|
||||||
|
|
||||||
msj_resume_previously_suspended_order: "Reanuda una orden previamente suspendida",
|
msj_resume_previously_suspended_order: "Reanuda una orden previamente suspendida",
|
||||||
msj_transfer_Order_another_station: "Transfiere la orden a otra estación",
|
msj_transfer_Order_another_station: "Transfiere la orden a otra estación",
|
||||||
|
|
@ -77,19 +125,35 @@ i18n
|
||||||
msj_manage_coupons: "Administrar cupones",
|
msj_manage_coupons: "Administrar cupones",
|
||||||
msj_manage_discount_percentage: "Administrar porcentaje de descuento",
|
msj_manage_discount_percentage: "Administrar porcentaje de descuento",
|
||||||
msj_manage_amount_discount: "Administrar importe de descuento",
|
msj_manage_amount_discount: "Administrar importe de descuento",
|
||||||
|
msj_bag_fee: "Tarifa de bolsa",
|
||||||
msj_bag_credit_management: "Gestión de bolsa de crédito",
|
msj_bag_credit_management: "Gestión de bolsa de crédito",
|
||||||
msj_bottle_refund_management: "Gestión de devolución de botellas",
|
msj_bottle_refund_management: "Gestión de devolución de botellas",
|
||||||
msj_tax_exemption_management: "Gestión de exención de impuestos",
|
msj_tax_exemption_management: "Gestión de exención de impuestos",
|
||||||
|
msj_delete_tax_exception: "Eliminar excepción de impuestos",
|
||||||
msj_query_search_article: "Consulta o búsqueda de artículo",
|
msj_query_search_article: "Consulta o búsqueda de artículo",
|
||||||
msj_enter_your_username: "Ingrese su nombre de usuario",
|
msj_enter_your_username: "Ingrese su nombre de usuario",
|
||||||
msj_enter_your_password: "Ingrese su contraseña",
|
msj_enter_your_password: "Ingrese su contraseña",
|
||||||
msj_Incorrect_credentials_try_again: "Credenciales incorrectas, inténtelo de nuevo",
|
msj_Incorrect_credentials_try_again: "Credenciales incorrectas, inténtelo de nuevo",
|
||||||
|
msj_forgot_password: "¿Olvidaste tu contraseña?",
|
||||||
|
msj_Password_reset_email_sent: "Se ha enviado un correo electrónico de restablecimiento de contraseña",
|
||||||
|
msj_Error_sending_password_reset: "Error al enviar el restablecimiento de contraseña",
|
||||||
|
msj_back_to_login: "Volver al inicio de sesión",
|
||||||
|
msj_this_box_is: "Esta caja esta",
|
||||||
|
msj_press_any_key_button_start: "Presione cualquier tecla o el buton para comenzar",
|
||||||
|
msj_are_you_registered_as_affiliate: "¿Está registrado como afiliado?",
|
||||||
|
msj_improving_in_store_experience: "Mejorando la experiencia en la tienda",
|
||||||
|
msj_select_type_identity_document: "Seleccione el tipo de documento de identidad",
|
||||||
|
msj_enter_number_of: "Ingrese el número de",
|
||||||
|
msj_enter_your_email: "Ingrese su correo electrónico",
|
||||||
|
msj_enter_document_number: "Ingrese el número de documento",
|
||||||
|
msj_change_to: "Cambiar a",
|
||||||
|
|
||||||
|
tle_self_checkout: "AUTO PAGO",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
interpolation: {
|
interpolation: {
|
||||||
escapeValue: false, // Not needed for React as it escapes by default
|
escapeValue: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue