feat: start internatinalization

This commit is contained in:
canove
2020-08-12 19:55:41 -03:00
parent ecf5ea1eb3
commit 63732568e9
22 changed files with 356 additions and 11975 deletions

5
frontend/.gitignore vendored
View File

@@ -5,6 +5,7 @@
/.pnp
.pnp.js
# testing
/coverage
@@ -22,6 +23,6 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
package-lock.json
yarn.lock
yarn.lock

View File

@@ -13,6 +13,8 @@
"date-fns": "^2.14.0",
"emoji-mart": "^3.0.0",
"formik": "^2.1.5",
"i18next": "^19.6.3",
"i18next-browser-languagedetector": "^5.0.1",
"mic-recorder-to-mp3": "^2.2.1",
"qrcode.react": "^1.0.0",
"react": "^16.13.1",

View File

@@ -2,10 +2,11 @@ import React, { useState, useEffect } from "react";
import { Formik, FieldArray } from "formik";
import { makeStyles } from "@material-ui/core/styles";
import { green } from "@material-ui/core/colors";
import Button from "@material-ui/core/Button";
import TextField from "@material-ui/core/TextField";
import Dialog from "@material-ui/core/Dialog";
import DialogActions from "@material-ui/core/DialogActions";
import DialogContent from "@material-ui/core/DialogContent";
import DialogTitle from "@material-ui/core/DialogTitle";
@@ -13,9 +14,8 @@ import Typography from "@material-ui/core/Typography";
import IconButton from "@material-ui/core/IconButton";
import DeleteOutlineIcon from "@material-ui/icons/DeleteOutline";
import CircularProgress from "@material-ui/core/CircularProgress";
import { green } from "@material-ui/core/colors";
import { makeStyles } from "@material-ui/core/styles";
import { i18n } from "../../translate/i18n";
import api from "../../services/api";
@@ -121,14 +121,16 @@ const ContactModal = ({ open, onClose, contactId }) => {
}) => (
<form onSubmit={handleSubmit}>
<DialogTitle id="form-dialog-title">
{contactId ? "Editar contato" : "Adicionar contato"}
{contactId
? `${i18n.t("contactModal.title.edit")}`
: `${i18n.t("contactModal.title.add")}`}
</DialogTitle>
<DialogContent dividers>
<Typography variant="subtitle1" gutterBottom>
Dados do contato
{i18n.t("contactModal.form.mainInfo")}
</Typography>
<TextField
label="Nome"
label={i18n.t("contactModal.form.name")}
name="name"
value={values.name || ""}
onChange={handleChange}
@@ -138,18 +140,18 @@ const ContactModal = ({ open, onClose, contactId }) => {
className={classes.textField}
/>
<TextField
label="Número do Whatsapp"
label={i18n.t("contactModal.form.number")}
name="number"
value={values.number || ""}
onChange={handleChange}
placeholder="Ex: 5513912344321"
placeholder="5513912344321"
variant="outlined"
margin="dense"
required
/>
<div>
<TextField
label="Email"
label={i18n.t("contactModal.form.email")}
name="email"
value={values.email || ""}
onChange={handleChange}
@@ -163,7 +165,7 @@ const ContactModal = ({ open, onClose, contactId }) => {
style={{ marginBottom: 8, marginTop: 12 }}
variant="subtitle1"
>
Informações adicionais
{i18n.t("contactModal.form.extraInfo")}
</Typography>
<FieldArray name="extraInfo">
@@ -177,7 +179,7 @@ const ContactModal = ({ open, onClose, contactId }) => {
key={`${index}-info`}
>
<TextField
label="Nome do campo"
label={i18n.t("contactModal.form.extraName")}
name={`extraInfo[${index}].name`}
value={info.name || ""}
onChange={handleChange}
@@ -187,7 +189,7 @@ const ContactModal = ({ open, onClose, contactId }) => {
className={classes.textField}
/>
<TextField
label="Valor"
label={i18n.t("contactModal.form.extraValue")}
name={`extraInfo[${index}].value`}
value={info.value || ""}
onChange={handleChange}
@@ -211,7 +213,7 @@ const ContactModal = ({ open, onClose, contactId }) => {
color="primary"
onClick={() => push({ name: "", value: "" })}
>
+ Adicionar atributo
{`+ ${i18n.t("contactModal.buttons.addExtraInfo")}`}
</Button>
</div>
</>
@@ -225,7 +227,7 @@ const ContactModal = ({ open, onClose, contactId }) => {
disabled={isSubmitting}
variant="outlined"
>
Cancelar
{i18n.t("contactModal.buttons.cancel")}
</Button>
<Button
type="submit"
@@ -234,7 +236,9 @@ const ContactModal = ({ open, onClose, contactId }) => {
variant="contained"
className={classes.btnWrapper}
>
{contactId ? "Salvar" : "Adicionar"}
{contactId
? `${i18n.t("contactModal.buttons.okEdit")}`
: `${i18n.t("contactModal.buttons.okAdd")}`}
{isSubmitting && (
<CircularProgress
size={24}

View File

@@ -1,28 +1,16 @@
import React from "react";
import { Link as RouterLink } from "react-router-dom";
import { makeStyles } from "@material-ui/core/styles";
import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import ListItemText from "@material-ui/core/ListItemText";
// import ListSubheader from "@material-ui/core/ListSubheader";
import Collapse from "@material-ui/core/Collapse";
import DashboardIcon from "@material-ui/icons/Dashboard";
import WhatsAppIcon from "@material-ui/icons/WhatsApp";
import SyncAltIcon from "@material-ui/icons/SyncAlt";
import ChatIcon from "@material-ui/icons/Chat";
import LayersIcon from "@material-ui/icons/Layers";
import ExpandLess from "@material-ui/icons/ExpandLess";
import ExpandMore from "@material-ui/icons/ExpandMore";
import ContactPhoneIcon from "@material-ui/icons/ContactPhone";
const useStyles = makeStyles(theme => ({
nested: {
paddingLeft: theme.spacing(4),
},
}));
import { i18n } from "../../translate/i18n";
function ListItemLink(props) {
const { icon, primary, to, className } = props;
@@ -46,76 +34,27 @@ function ListItemLink(props) {
}
const MainListItems = () => {
const classes = useStyles();
const [open, setOpen] = React.useState(false);
const handleClick = () => {
setOpen(!open);
};
return (
<div>
<ListItemLink to="/" primary="Dashboard" icon={<DashboardIcon />} />
<ListItemLink
to="/whats-auth"
primary={i18n.t("mainDrawer.listItems.connection")}
icon={<SyncAltIcon />}
/>
<ListItemLink
to="/chat"
primary={i18n.t("mainDrawer.listItems.tickets")}
icon={<WhatsAppIcon />}
/>
<ListItem button onClick={handleClick}>
<ListItemIcon>
<WhatsAppIcon />
</ListItemIcon>
<ListItemText primary="WhatsApp" />
{open ? <ExpandLess /> : <ExpandMore />}
</ListItem>
<Collapse in={open} timeout="auto" unmountOnExit>
<List component="div" disablePadding>
<ListItemLink
className={classes.nested}
to="/whats-auth"
primary="Conexão"
icon={<SyncAltIcon />}
/>
<ListItemLink
className={classes.nested}
to="/chat"
primary="Chat"
icon={<ChatIcon />}
/>
</List>
</Collapse>
<ListItemLink
to="/contacts"
primary="Contatos"
primary={i18n.t("mainDrawer.listItems.contacts")}
icon={<ContactPhoneIcon />}
/>
<ListItem button disabled>
<ListItemIcon>
<LayersIcon />
</ListItemIcon>
<ListItemText primary="Integrações" />
</ListItem>
</div>
);
};
// export const secondaryListItems = (
// <div>
// <ListSubheader inset>Saved reports</ListSubheader>
// <ListItem button>
// <ListItemIcon>
// <AssignmentIcon />
// </ListItemIcon>
// <ListItemText primary="Current month" />
// </ListItem>
// <ListItem button>
// <ListItemIcon>
// <AssignmentIcon />
// </ListItemIcon>
// <ListItemText primary="Last quarter" />
// </ListItem>
// <ListItem button>
// <ListItemIcon>
// <AssignmentIcon />
// </ListItemIcon>
// <ListItemText primary="Year-end sale" />
// </ListItem>
// </div>
// );
export default MainListItems;

View File

@@ -221,8 +221,6 @@ const useStyles = makeStyles(theme => ({
},
}));
let socket;
const MessagesList = () => {
const { ticketId } = useParams();
const history = useHistory();
@@ -272,17 +270,9 @@ const MessagesList = () => {
}, [pageNumber, ticketId, history]);
useEffect(() => {
socket = openSocket(process.env.REACT_APP_BACKEND_URL);
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
socket.emit("joinChatBox", ticketId, () => {});
return () => {
socket.disconnect();
setPageNumber(1);
setMessagesList([]);
};
}, [ticketId]);
useEffect(() => {
socket.on("appMessage", data => {
if (loading) return;
@@ -300,7 +290,13 @@ const MessagesList = () => {
setContact(data.contact);
}
});
}, [loading]);
return () => {
socket.disconnect();
setPageNumber(1);
setMessagesList([]);
};
}, [ticketId, loading]);
const loadMore = () => {
setPageNumber(prevPageNumber => prevPageNumber + 1);

View File

@@ -14,6 +14,7 @@ import { green } from "@material-ui/core/colors";
import { makeStyles } from "@material-ui/core/styles";
import { i18n } from "../../translate/i18n";
import api from "../../services/api";
const useStyles = makeStyles(theme => ({
@@ -101,7 +102,9 @@ const NewTicketModal = ({ modalOpen, onClose, contactId }) => {
scroll="paper"
>
<form onSubmit={handleSaveTicket}>
<DialogTitle id="form-dialog-title">Criar Ticket</DialogTitle>
<DialogTitle id="form-dialog-title">
{i18n.t("newTicketModal.title")}
</DialogTitle>
<DialogContent dividers>
<Autocomplete
id="asynchronous-demo"
@@ -115,7 +118,7 @@ const NewTicketModal = ({ modalOpen, onClose, contactId }) => {
renderInput={params => (
<TextField
{...params}
label="Digite para pesquisar o contato"
label={i18n.t("newTicketModal.fieldLabel")}
variant="outlined"
required
autoFocus
@@ -143,7 +146,7 @@ const NewTicketModal = ({ modalOpen, onClose, contactId }) => {
disabled={loading}
variant="outlined"
>
Cancelar
{i18n.t("newTicketModal.buttons.cancel")}
</Button>
<Button
type="submit"
@@ -152,7 +155,7 @@ const NewTicketModal = ({ modalOpen, onClose, contactId }) => {
variant="contained"
className={classes.btnWrapper}
>
Salvar
{i18n.t("newTicketModal.buttons.ok")}
{loading && (
<CircularProgress
size={24}

View File

@@ -2,11 +2,13 @@ import React from "react";
import QRCode from "qrcode.react";
import Typography from "@material-ui/core/Typography";
import { i18n } from "../../translate/i18n";
const Qrcode = ({ qrCode }) => {
return (
<div>
<Typography color="primary" gutterBottom>
Leia o QrCode para iniciar a sessão
{i18n.t("qrCode.message")}
</Typography>
<QRCode value={qrCode} size={256} />
</div>

View File

@@ -1,18 +1,20 @@
import React from "react";
import Typography from "@material-ui/core/Typography";
import { i18n } from "../../translate/i18n";
const SessionInfo = ({ session }) => {
console.log(session);
return (
<div>
<Typography component="h2" variant="h6" color="primary" gutterBottom>
{`Status: ${session.status}`}
{`${i18n.t("sessionInfo.status")}${session.status}`}
</Typography>
<Typography component="p" variant="h6">
{`Bateria: ${session.battery}%`}
{`${i18n.t("sessionInfo.battery")}${session.battery}%`}
</Typography>
<Typography color="textSecondary">
{`Carregando: ${session.plugged} `}
{`${i18n.t("sessionInfo.charging")}${session.plugged} `}
</Typography>
</div>
);

View File

@@ -23,10 +23,9 @@ import NewTicketModal from "../NewTicketModal";
import TicketsList from "../TicketsList";
import TabPanel from "../TabPanel";
import { i18n } from "../../translate/i18n";
import api from "../../services/api";
let socket;
const useStyles = makeStyles(theme => ({
ticketsWrapper: {
position: "relative",
@@ -209,15 +208,9 @@ const Tickets = () => {
}, [searchParam, pageNumber, token, tab]);
useEffect(() => {
socket = openSocket(process.env.REACT_APP_BACKEND_URL);
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
socket.emit("joinNotification");
return () => {
socket.disconnect();
};
}, []);
useEffect(() => {
socket.on("ticket", data => {
if (loading) return;
@@ -232,7 +225,7 @@ const Tickets = () => {
if (data.action === "delete") {
deleteTicket(data);
if (ticketId && data.ticketId === +ticketId) {
toast.warn("O ticket que você estava foi deletado.");
toast.warn(i18n.t("tickets.toasts.deleted"));
history.push("/chat");
}
}
@@ -253,6 +246,10 @@ const Tickets = () => {
showDesktopNotification(data);
}
});
return () => {
socket.disconnect();
};
}, [history, ticketId, userId, loading]);
const loadMore = () => {
@@ -262,16 +259,11 @@ const Tickets = () => {
const updateTickets = ({ ticket }) => {
setTickets(prevState => {
const ticketIndex = prevState.findIndex(t => t.id === ticket.id);
if (prevState.length >= 20) {
if (ticketIndex !== -1) {
let aux = [...prevState];
aux[ticketIndex] = ticket;
aux.unshift(aux.splice(ticketIndex, 1)[0]);
return aux;
} else {
return [ticket, ...prevState];
}
if (ticketIndex !== -1) {
let aux = [...prevState];
aux[ticketIndex] = ticket;
aux.unshift(aux.splice(ticketIndex, 1)[0]);
return aux;
} else {
return [ticket, ...prevState];
}
@@ -298,7 +290,10 @@ const Tickets = () => {
icon: contact.profilePicUrl,
tag: ticket.id,
};
let notification = new Notification(`Mensagem de ${contact.name}`, options);
let notification = new Notification(
`${i18n.t("tickets.notification.message")} ${contact.name}`,
options
);
notification.onclick = function (event) {
event.preventDefault(); //
@@ -396,19 +391,19 @@ const Tickets = () => {
<Tab
value={"open"}
icon={<MoveToInboxIcon />}
label="Inbox"
label={i18n.t("tickets.tabs.open.title")}
classes={{ root: classes.tab }}
/>
<Tab
value={"closed"}
icon={<CheckBoxIcon />}
label="Resolvidos"
label={i18n.t("tickets.tabs.closed.title")}
classes={{ root: classes.tab }}
/>
<Tab
value={"search"}
icon={<SearchIcon />}
label="Busca"
label={i18n.t("tickets.tabs.search.title")}
classes={{ root: classes.tab }}
/>
</Tabs>
@@ -418,7 +413,7 @@ const Tickets = () => {
<SearchIcon className={classes.searchIcon} />
<InputBase
className={classes.contactsSearchInput}
placeholder="Pesquisar tickets e mensagens"
placeholder={i18n.t("tickets.search.placeholder")}
type="search"
onChange={handleSearchContact}
/>
@@ -432,19 +427,19 @@ const Tickets = () => {
onScroll={handleScroll}
>
<div className={classes.ticketsListHeader}>
Atendendo
{i18n.t("tickets.tabs.open.assignedHeader")}
<span className={classes.ticketsCount}>
{countTickets("open", userId)}
</span>
<div className={classes.ticketsListActions}>
<FormControlLabel
label="Todos"
label={i18n.t("tickets.buttons.showAll")}
labelPlacement="start"
control={
<Switch
size="small"
checked={showAllTickets}
onChange={e => setShowAllTickets(prevState => !prevState)}
onChange={() => setShowAllTickets(prevState => !prevState)}
name="showAllTickets"
color="primary"
/>
@@ -467,8 +462,10 @@ const Tickets = () => {
showAllTickets={showAllTickets}
ticketId={ticketId}
handleAcepptTicket={handleAcepptTicket}
noTicketsTitle="Pronto pra mais?"
noTicketsMessage="Aceite um ticket da fila para começar."
noTicketsTitle={i18n.t("tickets.tabs.open.openNoTicketsTitle")}
noTicketsMessage={i18n.t(
"tickets.tabs.open.openNoTicketsMessage"
)}
status="open"
userId={userId}
/>
@@ -482,7 +479,7 @@ const Tickets = () => {
onScroll={handleScroll}
>
<div className={classes.ticketsListHeader}>
Aguardando
{i18n.t("tickets.tabs.open.pendingHeader")}
<span className={classes.ticketsCount}>
{countTickets("pending", null)}
</span>
@@ -495,8 +492,10 @@ const Tickets = () => {
showAllTickets={showAllTickets}
ticketId={ticketId}
handleAcepptTicket={handleAcepptTicket}
noTicketsTitle="Tudo resolvido!"
noTicketsMessage="Nenhum ticket pendente."
noTicketsTitle={i18n.t("tickets.tabs.open.pendingNoTicketsTitle")}
noTicketsMessage={i18n.t(
"tickets.tabs.open.pendingNoTicketsMessage"
)}
status="pending"
userId={null}
/>
@@ -541,8 +540,8 @@ const Tickets = () => {
showAllTickets={showAllTickets}
ticketId={ticketId}
handleAcepptTicket={handleAcepptTicket}
noTicketsTitle="Nada encontrado!"
noTicketsMessage="Tente buscar por outro termo."
noTicketsTitle={i18n.t("tickets.tabs.search.noTicketsTitle")}
noTicketsMessage={i18n.t("tickets.tabs.search.noTicketsMessage")}
status="all"
/>
{loading && <TicketsSkeleton />}

View File

@@ -12,6 +12,8 @@ import Divider from "@material-ui/core/Divider";
import Badge from "@material-ui/core/Badge";
import Button from "@material-ui/core/Button";
import { i18n } from "../../translate/i18n";
const useStyles = makeStyles(theme => ({
ticket: {
position: "relative",
@@ -185,7 +187,7 @@ const TicketsList = ({
className="hidden-button"
onClick={e => handleAcepptTicket(ticket.id)}
>
Aceitar
{i18n.t("ticketsList.buttons.accept")}
</Button>
) : null}
</ListItem>

View File

@@ -3,6 +3,7 @@ import { useHistory } from "react-router-dom";
import { toast } from "react-toastify";
import { i18n } from "../../translate/i18n";
import api from "../../services/api";
const useAuth = () => {
@@ -23,7 +24,6 @@ const useAuth = () => {
history.location.pathname === "/signup"
) {
setLoading(false);
return;
}
try {
@@ -35,7 +35,7 @@ const useAuth = () => {
} catch (err) {
setLoading(false);
setIsAuth(false);
toast.error("Erro de autenticação. Por favor, faça login novamente");
toast.error(i18n.t("auth.toasts.fail"));
}
};
checkAuth();
@@ -50,9 +50,10 @@ const useAuth = () => {
localStorage.setItem("userId", res.data.userId);
api.defaults.headers.Authorization = `Bearer ${res.data.token}`;
setIsAuth(true);
toast.success(i18n.t("auth.toasts.success"));
history.push("/chat");
} catch (err) {
toast.error("Erro de autenticação. Verifique os dados de login");
toast.error(i18n.t("auth.toasts.fail"));
}
};

View File

@@ -7,6 +7,8 @@ import { makeStyles } from "@material-ui/core/styles";
import Tickets from "../../components/Tickets/";
import MessagesList from "../../components/MessagesList/";
import { i18n } from "../../translate/i18n";
const useStyles = makeStyles(theme => ({
chatContainer: {
flex: 1,
@@ -61,7 +63,7 @@ const Chat = () => {
</>
) : (
<Paper square variant="outlined" className={classes.welcomeMsg}>
<span>Selecione um contato para começar a conversar</span>
<span>{i18n.t("chat.noTicketMessage")}</span>
</Paper>
)}
</Grid>

View File

@@ -28,7 +28,7 @@ import ContactsSekeleton from "../../components/ContactsSekeleton";
import ContactModal from "../../components/ContactModal";
import ConfirmationModal from "../../components/ConfirmationModal/";
let socket;
import { i18n } from "../../translate/i18n";
const useStyles = makeStyles(theme => ({
mainContainer: {
@@ -105,13 +105,7 @@ const Contacts = () => {
}, [searchParam, page, rowsPerPage]);
useEffect(() => {
socket = openSocket(process.env.REACT_APP_BACKEND_URL);
return () => {
socket.disconnect();
};
}, []);
useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
socket.on("contact", data => {
if ((data.action === "update" || data.action === "create") && !loading) {
updateContacts(data.contact);
@@ -121,6 +115,10 @@ const Contacts = () => {
deleteContact(data.contactId);
}
});
return () => {
socket.disconnect();
};
}, [loading]);
const updateContacts = contact => {
@@ -206,8 +204,10 @@ const Contacts = () => {
<ConfirmationModal
title={
deletingContact
? `Deletar ${deletingContact.name}?`
: `Importar contatos`
? `${i18n.t("contacts.confirmationModal.deleteTitle")} ${
deletingContact.name
}?`
: `${i18n.t("contacts.confirmationModal.importTitlte")}`
}
open={confirmOpen}
setOpen={setConfirmOpen}
@@ -218,17 +218,17 @@ const Contacts = () => {
}
>
{deletingContact
? "Tem certeza que deseja deletar este contato? Todos os tickets relacionados serão perdidos."
: "Deseja importas todos os contatos do telefone? Essa função é experimental, você terá que recarregar a página após a importação "}
? `${i18n.t("contacts.confirmationModal.deleteMessage")}`
: `${i18n.t("contacts.confirmationModal.importMessage")}`}
</ConfirmationModal>
<div className={classes.contactsHeader}>
<Typography variant="h5" gutterBottom>
Contatos
{i18n.t("contacts.title")}
</Typography>
<div className={classes.actionButtons}>
<TextField
placeholder="Pesquisar..."
placeholder={i18n.t("contacts.searchPlaceholder")}
type="search"
value={searchParam}
onChange={handleSearch}
@@ -245,14 +245,14 @@ const Contacts = () => {
color="primary"
onClick={e => setConfirmOpen(true)}
>
Importar contatos
{i18n.t("contacts.buttons.import")}
</Button>
<Button
variant="contained"
color="primary"
onClick={handleOpenContactModal}
>
Adicionar contato
{i18n.t("contacts.buttons.add")}
</Button>
</div>
</div>
@@ -261,10 +261,12 @@ const Contacts = () => {
<TableHead>
<TableRow>
<TableCell padding="checkbox" />
<TableCell>Nome</TableCell>
<TableCell>Whatsapp</TableCell>
<TableCell>Email</TableCell>
<TableCell align="right">Ações</TableCell>
<TableCell>{i18n.t("contacts.table.name")}</TableCell>
<TableCell>{i18n.t("contacts.table.whatsapp")}</TableCell>
<TableCell>{i18n.t("contacts.table.email")}</TableCell>
<TableCell align="right">
{i18n.t("contacts.table.actions")}
</TableCell>
</TableRow>
</TableHead>
<TableBody>

View File

@@ -11,6 +11,8 @@ import {
} from "recharts";
import { startOfHour, parseISO, format } from "date-fns";
import { i18n } from "../../translate/i18n";
import Title from "./Title";
import api from "../../services/api";
@@ -69,7 +71,9 @@ const Chart = () => {
return (
<React.Fragment>
<Title>{`Tickets hoje: ${tickets.length}`}</Title>
<Title>{`${i18n.t("dashboard.charts.perDay.title")}${
tickets.length
}`}</Title>
<ResponsiveContainer>
<BarChart
data={chartData}

View File

@@ -5,8 +5,6 @@ import Avatar from "@material-ui/core/Avatar";
import Button from "@material-ui/core/Button";
import CssBaseline from "@material-ui/core/CssBaseline";
import TextField from "@material-ui/core/TextField";
// import FormControlLabel from "@material-ui/core/FormControlLabel";
// import Checkbox from "@material-ui/core/Checkbox";
import Link from "@material-ui/core/Link";
import Grid from "@material-ui/core/Grid";
import Box from "@material-ui/core/Box";
@@ -15,13 +13,15 @@ import Typography from "@material-ui/core/Typography";
import { makeStyles } from "@material-ui/core/styles";
import Container from "@material-ui/core/Container";
import { i18n } from "../../translate/i18n";
import { AuthContext } from "../../context/Auth/AuthContext";
const Copyright = () => {
return (
<Typography variant="body2" color="textSecondary" align="center">
{"Copyright © "}
<Link color="inherit" href="https://material-ui.com/">
<Link color="inherit" href="https://economicros.com.br/">
Canove
</Link>{" "}
{new Date().getFullYear()}
@@ -50,7 +50,7 @@ const useStyles = makeStyles(theme => ({
},
}));
const Login = ({ showToast }) => {
const Login = () => {
const classes = useStyles();
const [user, setUser] = useState({ email: "", password: "" });
@@ -69,7 +69,7 @@ const Login = ({ showToast }) => {
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Login
{i18n.t("login.title")}
</Typography>
<form
className={classes.form}
@@ -82,7 +82,7 @@ const Login = ({ showToast }) => {
required
fullWidth
id="email"
label="Email"
label={i18n.t("login.form.email")}
name="email"
value={user.email}
onChange={handleChangeInput}
@@ -95,17 +95,13 @@ const Login = ({ showToast }) => {
required
fullWidth
name="password"
label="Senha"
label={i18n.t("login.form.password")}
type="password"
id="password"
value={user.password}
onChange={handleChangeInput}
autoComplete="current-password"
/>
{/* <FormControlLabel
control={<Checkbox value="remember" color="primary" />}
label="Lembrar"
/> */}
<Button
type="submit"
fullWidth
@@ -113,14 +109,9 @@ const Login = ({ showToast }) => {
color="primary"
className={classes.submit}
>
Entrar
{i18n.t("login.buttons.submit")}
</Button>
<Grid container>
{/* <Grid item xs>
<Link href="#" variant="body2">
Forgot password?
</Link>
</Grid> */}
<Grid item>
<Link
href="#"
@@ -128,7 +119,7 @@ const Login = ({ showToast }) => {
component={RouterLink}
to="/signup"
>
{"Não tem uma conta? Cadastre-se!"}
{i18n.t("login.buttons.register")}
</Link>
</Grid>
</Grid>

View File

@@ -16,6 +16,8 @@ import Typography from "@material-ui/core/Typography";
import { makeStyles } from "@material-ui/core/styles";
import Container from "@material-ui/core/Container";
import { i18n } from "../../translate/i18n";
import api from "../../services/api";
const Copyright = () => {
@@ -65,10 +67,10 @@ const SignUp = () => {
e.preventDefault();
try {
await api.post("/auth/signup", user);
toast.success("Usuário criado com sucesso! Faça seu login.");
toast.success(i18n.t("signup.toast.success"));
history.push("/login");
} catch (err) {
toast.error("Erro ao criar usuário. Verifique os dados informados.");
toast.error(i18n.t("signup.toast.fail"));
}
};
@@ -80,7 +82,7 @@ const SignUp = () => {
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Cadastre-se
{i18n.t("signup.title")}
</Typography>
<form className={classes.form} noValidate onSubmit={handleSignUp}>
<Grid container spacing={2}>
@@ -92,7 +94,7 @@ const SignUp = () => {
required
fullWidth
id="name"
label="Nome"
label={i18n.t("signup.form.name")}
value={user.name}
onChange={handleChangeInput}
autoFocus
@@ -105,7 +107,7 @@ const SignUp = () => {
required
fullWidth
id="email"
label="Email"
label={i18n.t("signup.form.email")}
name="email"
autoComplete="email"
value={user.email}
@@ -118,7 +120,7 @@ const SignUp = () => {
required
fullWidth
name="password"
label="Senha"
label={i18n.t("signup.form.password")}
type="password"
id="password"
autoComplete="current-password"
@@ -134,12 +136,12 @@ const SignUp = () => {
color="primary"
className={classes.submit}
>
Cadastrar
{i18n.t("signup.buttons.submit")}
</Button>
<Grid container justify="flex-end">
<Grid item>
<Link href="#" variant="body2" component={RouterLink} to="/login">
tem uma conta? Entre!
{i18n.t("signup.buttons.login")}
</Link>
</Grid>
</Grid>

View File

@@ -10,8 +10,6 @@ import Paper from "@material-ui/core/Paper";
import SessionInfo from "../../components/SessionInfo";
import Qrcode from "../../components/Qrcode";
let socket;
const useStyles = makeStyles(theme => ({
root: {
display: "flex",
@@ -55,14 +53,8 @@ const WhatsAuth = () => {
}, []);
useEffect(() => {
socket = openSocket(process.env.REACT_APP_BACKEND_URL);
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
return () => {
socket.disconnect();
};
}, []);
useEffect(() => {
socket.on("qrcode", data => {
if (data.action === "update") {
setQrCode(data.qr);
@@ -76,6 +68,10 @@ const WhatsAuth = () => {
history.push("/chat");
}
});
return () => {
socket.disconnect();
};
}, [history]);
return (

View File

@@ -0,0 +1,14 @@
import i18n from "i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import { messages } from "./languages";
i18n.use(LanguageDetector).init({
debug: false,
defaultNS: ["translations"],
fallbackLng: "pt",
ns: ["translations"],
resources: messages,
});
export { i18n };

View File

@@ -0,0 +1,24 @@
const messages = {
en: {
translations: {
signup: {
title: "Signup",
toasts: {
success: "User sucessfully registered. Login!",
fail: "Error creating user account. Verify your data.",
},
form: {
name: "Name",
email: "Email",
password: "Password",
},
buttons: {
submit: "SIGNUP",
login: "Already registred? Go to login!",
},
},
},
},
};
export { messages };

View File

@@ -0,0 +1,9 @@
import { messages as portugueseMessages } from "./pt";
import { messages as englishMessages } from "./en";
const messages = {
...portugueseMessages,
...englishMessages,
};
export { messages };

View File

@@ -0,0 +1,154 @@
const messages = {
pt: {
translations: {
signup: {
title: "Cadastre-se",
toasts: {
success: "Usuário criado com sucesso! Faça seu login!!!.",
fail: "Erro ao criar usuário. Verifique os dados informados.",
},
form: {
name: "Nome",
email: "Email",
password: "Senha",
},
buttons: {
submit: "Cadastrar",
login: "Já tem uma conta? Entre!",
},
},
login: {
title: "Login",
form: {
email: "Email",
password: "Senha",
},
buttons: {
submit: "Entrar",
register: "Não tem um conta? Cadastre-se!",
},
},
auth: {
toasts: {
success: "Login efetuado com sucesso!",
fail: "Erro de autenticação. Por favor, faça login novamente",
},
},
dashboard: {
charts: {
perDay: {
title: "Tickets hoje: ",
},
},
},
sessionInfo: {
status: "Status: ",
battery: "Bateria: ",
charging: "Carregando: ",
},
qrCode: {
message: "Leia o QrCode para iniciar a sessão",
},
contacts: {
title: "Contatos",
searchPlaceholder: "Pesquisar...",
confirmationModal: {
deleteTitle: "Deletar ",
importTitlte: "Importar contatos",
deleteMessage:
"Tem certeza que deseja deletar este contato? Todos os tickets relacionados serão perdidos.",
importMessage:
"Deseja importas todos os contatos do telefone? Essa função é experimental, você terá que recarregar a página após a importação.",
},
buttons: {
import: "Importar Contatos",
add: "Adicionar Contato",
},
table: {
name: "Nome",
whatsapp: "WhatsApp",
email: "Email",
actions: "Ações",
},
},
contactModal: {
title: {
add: "Adicionar contato",
edit: "Editar contato",
},
form: {
mainInfo: "Dados do contato",
extraInfo: "Informações adicionais",
name: "Nome",
number: "Número do Whatsapp",
email: "Email",
extraName: "Nome do campo",
extraValue: "Valor",
},
buttons: {
addExtraInfo: "Adicionar informação",
okAdd: "Adicionar",
okEdit: "Salvar",
cancel: "Cancelar",
},
},
chat: {
noTicketMessage: "Selecione um ticket para começar a conversar.",
},
tickets: {
toasts: {
deleted: "O ticket que você estava foi deletado.",
},
notification: {
message: "Mensagem de",
},
tabs: {
open: {
title: "Inbox",
assignedHeader: "Atendendo",
pendingHeader: "Aguardando",
openNoTicketsTitle: "Pronto pra mais?",
openNoTicketsMessage: "Aceite um ticket da fila para começar.",
pendingNoTicketsTitle: "Tudo resolvido!",
pendingNoTicketsMessage: "Nenhum ticket pendente.",
},
closed: { title: "Resolvidos" },
search: {
title: "Busca",
noTicketsTitle: "Nada encontrado!",
noTicketsMessage: "Tente pesquisar por outro termo.",
},
},
search: {
placeholder: "Pesquisar tickets e mensagens.",
},
buttons: {
showAll: "Todos",
},
},
ticketsList: {
buttons: {
accept: "Aceitar",
},
},
newTicketModal: {
title: "Criar Ticket",
fieldLabel: "Digite para pesquisar o contato",
buttons: {
ok: "Salvar",
cancel: "Cancelar",
},
},
mainDrawer: {
listItems: {
dashboard: "Dashboard",
connection: "Conexão",
tickets: "Tickets",
contacts: "Contatos",
},
},
},
},
};
export { messages };

File diff suppressed because it is too large Load Diff