Tickets in progress

I needed understand more about Socket.IO and how this aplication work to
update de visual
pull/14/head^2
RenatoDiGiacomo 2022-07-21 14:00:06 -03:00
parent 87e0d61281
commit a66355f43b
9 changed files with 683 additions and 325 deletions

View File

@ -91,8 +91,6 @@ const Ticket = () => {
const fetchTicket = async () => { const fetchTicket = async () => {
try { try {
// maria julia
const { data } = await api.get("/tickets/" + ticketId); const { data } = await api.get("/tickets/" + ticketId);
// setContact(data.contact); // setContact(data.contact);

View File

@ -12,7 +12,7 @@ import useTickets from "../../hooks/useTickets";
import { i18n } from "../../translate/i18n"; import { i18n } from "../../translate/i18n";
import { AuthContext } from "../../context/Auth/AuthContext"; import { AuthContext } from "../../context/Auth/AuthContext";
const useStyles = makeStyles(theme => ({ const useStyles = makeStyles((theme) => ({
ticketsListWrapper: { ticketsListWrapper: {
position: "relative", position: "relative",
display: "flex", display: "flex",
@ -75,12 +75,8 @@ const reducer = (state, action) => {
if (action.type === "LOAD_TICKETS") { if (action.type === "LOAD_TICKETS") {
const newTickets = action.payload; const newTickets = action.payload;
newTickets.forEach((ticket) => {
newTickets.forEach(ticket => { const ticketIndex = state.findIndex((t) => t.id === ticket.id);
// console.log('* ticket.unreadMessages: ',ticket.unreadMessages)
const ticketIndex = state.findIndex(t => t.id === ticket.id);
if (ticketIndex !== -1) { if (ticketIndex !== -1) {
state[ticketIndex] = ticket; state[ticketIndex] = ticket;
if (ticket.unreadMessages > 0) { if (ticket.unreadMessages > 0) {
@ -97,7 +93,7 @@ const reducer = (state, action) => {
if (action.type === "RESET_UNREAD") { if (action.type === "RESET_UNREAD") {
const ticketId = action.payload; const ticketId = action.payload;
const ticketIndex = state.findIndex(t => t.id === ticketId); const ticketIndex = state.findIndex((t) => t.id === ticketId);
if (ticketIndex !== -1) { if (ticketIndex !== -1) {
state[ticketIndex].unreadMessages = 0; state[ticketIndex].unreadMessages = 0;
} }
@ -108,9 +104,7 @@ const reducer = (state, action) => {
if (action.type === "UPDATE_TICKET") { if (action.type === "UPDATE_TICKET") {
const ticket = action.payload; const ticket = action.payload;
// console.log('++++++++++++ UPDATE_TICKET: ',ticket) const ticketIndex = state.findIndex((t) => t.id === ticket.id);
const ticketIndex = state.findIndex(t => t.id === ticket.id);
if (ticketIndex !== -1) { if (ticketIndex !== -1) {
state[ticketIndex] = ticket; state[ticketIndex] = ticket;
} else { } else {
@ -121,26 +115,19 @@ const reducer = (state, action) => {
} }
if (action.type === "UPDATE_TICKET_UNREAD_MESSAGES") { if (action.type === "UPDATE_TICKET_UNREAD_MESSAGES") {
const message = action.payload.message;
const message = action.payload.message
const ticket = action.payload.ticket; const ticket = action.payload.ticket;
const ticketIndex = state.findIndex(t => t.id === ticket.id); const ticketIndex = state.findIndex((t) => t.id === ticket.id);
if (ticketIndex !== -1) { if (ticketIndex !== -1) {
if (!message.fromMe) {
// console.log('>>>>>> ticketIndex: ', ticketIndex) ticket.unreadMessages += 1;
// console.log('&&&&&&& UPDATE_TICKET_UNREAD_MESSAGES ticket: ',ticket, ' |\n MESSAGE: ', message)
if(!message.fromMe){
ticket.unreadMessages +=1
} }
state[ticketIndex] = ticket; state[ticketIndex] = ticket;
state.unshift(state.splice(ticketIndex, 1)[0]); state.unshift(state.splice(ticketIndex, 1)[0]);
} else { } else {
state.unshift(ticket); state.unshift(ticket);
} }
@ -150,7 +137,7 @@ const reducer = (state, action) => {
if (action.type === "UPDATE_TICKET_CONTACT") { if (action.type === "UPDATE_TICKET_CONTACT") {
const contact = action.payload; const contact = action.payload;
const ticketIndex = state.findIndex(t => t.contactId === contact.id); const ticketIndex = state.findIndex((t) => t.contactId === contact.id);
if (ticketIndex !== -1) { if (ticketIndex !== -1) {
state[ticketIndex].contact = contact; state[ticketIndex].contact = contact;
} }
@ -159,7 +146,7 @@ const reducer = (state, action) => {
if (action.type === "DELETE_TICKET") { if (action.type === "DELETE_TICKET") {
const ticketId = action.payload; const ticketId = action.payload;
const ticketIndex = state.findIndex(t => t.id === ticketId); const ticketIndex = state.findIndex((t) => t.id === ticketId);
if (ticketIndex !== -1) { if (ticketIndex !== -1) {
state.splice(ticketIndex, 1); state.splice(ticketIndex, 1);
} }
@ -172,9 +159,7 @@ const reducer = (state, action) => {
} }
}; };
const TicketsList = (props) => { const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCount, style }) => {
const { status, searchParam, showAll, selectedQueueIds, updateCount, style } =
props;
const classes = useStyles(); const classes = useStyles();
const [pageNumber, setPageNumber] = useState(1); const [pageNumber, setPageNumber] = useState(1);
const [ticketsList, dispatch] = useReducer(reducer, []); const [ticketsList, dispatch] = useReducer(reducer, []);
@ -204,11 +189,11 @@ const TicketsList = (props) => {
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL); const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
const shouldUpdateTicket = ticket => const shouldUpdateTicket = (ticket) =>
(!ticket.userId || ticket.userId === user?.id || showAll) && (!ticket.userId || ticket.userId === user?.id || showAll) &&
(!ticket.queueId || selectedQueueIds.indexOf(ticket.queueId) > -1); (!ticket.queueId || selectedQueueIds.indexOf(ticket.queueId) > -1);
const notBelongsToUserQueues = ticket => const notBelongsToUserQueues = (ticket) =>
ticket.queueId && selectedQueueIds.indexOf(ticket.queueId) === -1; ticket.queueId && selectedQueueIds.indexOf(ticket.queueId) === -1;
socket.on("connect", () => { socket.on("connect", () => {
@ -217,10 +202,9 @@ const TicketsList = (props) => {
} else { } else {
socket.emit("joinNotification"); socket.emit("joinNotification");
} }
}); });
socket.on("ticket", data => { socket.on("ticket", (data) => {
if (data.action === "updateUnread") { if (data.action === "updateUnread") {
dispatch({ dispatch({
type: "RESET_UNREAD", type: "RESET_UNREAD",
@ -244,11 +228,8 @@ const TicketsList = (props) => {
} }
}); });
socket.on("appMessage", data => { socket.on("appMessage", (data) => {
if (data.action === "create" && shouldUpdateTicket(data.ticket)) { if (data.action === "create" && shouldUpdateTicket(data.ticket)) {
// console.log('((((((((((((((((((( DATA.MESSAGE: ', data.message)
dispatch({ dispatch({
type: "UPDATE_TICKET_UNREAD_MESSAGES", type: "UPDATE_TICKET_UNREAD_MESSAGES",
// payload: data.ticket, // payload: data.ticket,
@ -257,7 +238,7 @@ const TicketsList = (props) => {
} }
}); });
socket.on("contact", data => { socket.on("contact", (data) => {
if (data.action === "update") { if (data.action === "update") {
dispatch({ dispatch({
type: "UPDATE_TICKET_CONTACT", type: "UPDATE_TICKET_CONTACT",
@ -272,20 +253,16 @@ const TicketsList = (props) => {
}, [status, showAll, user, selectedQueueIds]); }, [status, showAll, user, selectedQueueIds]);
useEffect(() => { useEffect(() => {
if (typeof updateCount === "function") { if (typeof updateCount === "function") {
updateCount(ticketsList.length); updateCount(ticketsList.length);
} }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ticketsList]); }, [ticketsList]);
const loadMore = () => { const loadMore = () => {
setPageNumber(prevState => prevState + 1); setPageNumber((prevState) => prevState + 1);
}; };
const handleScroll = e => { const handleScroll = (e) => {
if (!hasMore || loading) return; if (!hasMore || loading) return;
const { scrollTop, scrollHeight, clientHeight } = e.currentTarget; const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
@ -307,16 +284,12 @@ const TicketsList = (props) => {
<List style={{ paddingTop: 0 }}> <List style={{ paddingTop: 0 }}>
{ticketsList.length === 0 && !loading ? ( {ticketsList.length === 0 && !loading ? (
<div className={classes.noTicketsDiv}> <div className={classes.noTicketsDiv}>
<span className={classes.noTicketsTitle}> <span className={classes.noTicketsTitle}>{i18n.t("ticketsList.noTicketsTitle")}</span>
{i18n.t("ticketsList.noTicketsTitle")} <p className={classes.noTicketsText}>{i18n.t("ticketsList.noTicketsMessage")}</p>
</span>
<p className={classes.noTicketsText}>
{i18n.t("ticketsList.noTicketsMessage")}
</p>
</div> </div>
) : ( ) : (
<> <>
{ticketsList.map(ticket => ( {ticketsList.map((ticket) => (
<TicketListItem ticket={ticket} key={ticket.id} /> <TicketListItem ticket={ticket} key={ticket.id} />
))} ))}
</> </>
@ -329,3 +302,4 @@ const TicketsList = (props) => {
}; };
export default TicketsList; export default TicketsList;

View File

@ -0,0 +1,338 @@
import React, { useContext, useEffect, useRef, useState } from "react";
import openSocket from "socket.io-client"
import { makeStyles } from "@material-ui/core/styles";
import Paper from "@material-ui/core/Paper";
import SearchIcon from "@material-ui/icons/Search";
import InputBase from "@material-ui/core/InputBase";
import Tabs from "@material-ui/core/Tabs";
import Tab from "@material-ui/core/Tab";
import Badge from "@material-ui/core/Badge";
import MoveToInboxIcon from "@material-ui/icons/MoveToInbox";
import CheckBoxIcon from "@material-ui/icons/CheckBox";
import FormControlLabel from "@material-ui/core/FormControlLabel";
import Switch from "@material-ui/core/Switch";
import { Button } from "@material-ui/core";
import NewTicketModal from "../NewTicketModal";
import TicketsList from "../TicketsList";
import TabPanel from "../TabPanel";
import { i18n } from "../../translate/i18n";
import { AuthContext } from "../../context/Auth/AuthContext";
import { Can } from "../Can";
import TicketsQueueSelect from "../TicketsQueueSelect";
const useStyles = makeStyles((theme) => ({
ticketsWrapper: {
position: "relative",
display: "flex",
height: "100%",
flexDirection: "column",
overflow: "hidden",
borderTopRightRadius: 0,
borderBottomRightRadius: 0,
},
tabsHeader: {
flex: "none",
backgroundColor: "#eee",
},
settingsIcon: {
alignSelf: "center",
marginLeft: "auto",
padding: 8,
},
tab: {
minWidth: 120,
width: 120,
},
ticketOptionsBox: {
display: "flex",
justifyContent: "space-between",
alignItems: "center",
background: "#fafafa",
padding: theme.spacing(1),
},
serachInputWrapper: {
flex: 1,
background: "#fff",
display: "flex",
borderRadius: 40,
padding: 4,
marginRight: theme.spacing(1),
},
searchIcon: {
color: "grey",
marginLeft: 6,
marginRight: 6,
alignSelf: "center",
},
searchInput: {
flex: 1,
border: "none",
borderRadius: 30,
},
badge: {
right: "-10px",
},
show: {
display: "block",
},
hide: {
display: "none !important",
},
}));
const TicketsManager = () => {
const classes = useStyles();
// Old New State
const [newPage, setNewPage] = React.useState(true);
// Old New State
const [searchParam, setSearchParam] = useState("");
const [tab, setTab] = useState("open");
const [tabOpen, setTabOpen] = useState("open");
const [newTicketModalOpen, setNewTicketModalOpen] = useState(false);
const [showAllTickets, setShowAllTickets] = useState(false);
const searchInputRef = useRef();
const { user } = useContext(AuthContext);
const [openCount, setOpenCount] = useState(0);
const [pendingCount, setPendingCount] = useState(0);
const userQueueIds = user.queues.map((q) => q.id);
const [selectedQueueIds, setSelectedQueueIds] = useState(userQueueIds || []);
const socket = openSocket.open(process.env.REACT_APP_BACKEND_URL)
console.log(user.queues)
console.log(selectedQueueIds)
socket.on("connect", () => {
if ("open") {
socket.emit("joinTickets", "open");
} else {
socket.emit("joinNotification");
}
});
socket.on("connect", ()=>{
})
useEffect(() => {
if (user.profile.toUpperCase() === "ADMIN") {
setShowAllTickets(true);
}
}, [user.profile]);
useEffect(() => {
if (tab === "search") {
searchInputRef.current.focus();
}
}, [tab]);
let searchTimeout;
const handleSearch = (e) => {
const searchedTerm = e.target.value.toLowerCase();
clearTimeout(searchTimeout);
if (searchedTerm === "") {
setSearchParam(searchedTerm);
setTab("open");
return;
}
searchTimeout = setTimeout(() => {
setSearchParam(searchedTerm);
}, 500);
};
const handleChangeTab = (e, newValue) => {
setTab(newValue);
};
const handleChangeTabOpen = (e, newValue) => {
setTabOpen(newValue);
};
const applyPanelStyle = (status) => {
if (tabOpen !== status) {
return { width: 0, height: 0 };
}
};
if (newPage) {
return (
<>
<NewTicketModal
modalOpen={newTicketModalOpen}
onClose={(e) => setNewTicketModalOpen(false)}
/>
<div>
<TicketsQueueSelect
style={{ marginLeft: 6 }}
selectedQueueIds={selectedQueueIds}
userQueues={user?.queues}
onChange={(values) => setSelectedQueueIds(values)}
/>
<TicketsList
status="open"
showAll={showAllTickets}
selectedQueueIds={selectedQueueIds}
updateCount={(val) => setOpenCount(val)}
style={applyPanelStyle("open")}
/>
<TicketsList
status="pending"
selectedQueueIds={selectedQueueIds}
updateCount={(val) => setPendingCount(val)}
style={applyPanelStyle("pending")}
/>
</div>
</>
);
}
return (
<Paper elevation={0} variant="outlined" className={classes.ticketsWrapper}>
<NewTicketModal
modalOpen={newTicketModalOpen}
onClose={(e) => setNewTicketModalOpen(false)}
/>
<Paper elevation={0} square className={classes.tabsHeader}>
<Tabs
value={tab}
onChange={handleChangeTab}
variant="fullWidth"
indicatorColor="primary"
textColor="primary"
aria-label="icon label tabs example"
>
<Tab
value={"open"}
icon={<MoveToInboxIcon />}
label={i18n.t("tickets.tabs.open.title")}
classes={{ root: classes.tab }}
/>
<Tab
value={"closed"}
icon={<CheckBoxIcon />}
label={i18n.t("tickets.tabs.closed.title")}
classes={{ root: classes.tab }}
/>
<Tab
value={"search"}
icon={<SearchIcon />}
label={i18n.t("tickets.tabs.search.title")}
classes={{ root: classes.tab }}
/>
</Tabs>
</Paper>
<Paper square elevation={0} className={classes.ticketOptionsBox}>
{tab === "search" ? (
<div className={classes.serachInputWrapper}>
<SearchIcon className={classes.searchIcon} />
<InputBase
className={classes.searchInput}
inputRef={searchInputRef}
placeholder={i18n.t("tickets.search.placeholder")}
type="search"
onChange={handleSearch}
/>
</div>
) : (
<>
<Button variant="outlined" color="primary" onClick={() => setNewTicketModalOpen(true)}>
{i18n.t("ticketsManager.buttons.newTicket")}
</Button>
<Can
role={user.profile}
perform="tickets-manager:showall"
yes={() => (
<FormControlLabel
label={i18n.t("tickets.buttons.showAll")}
labelPlacement="start"
control={
<Switch
size="small"
checked={showAllTickets}
onChange={() => setShowAllTickets((prevState) => !prevState)}
name="showAllTickets"
color="primary"
/>
}
/>
)}
/>
</>
)}
<TicketsQueueSelect
style={{ marginLeft: 6 }}
selectedQueueIds={selectedQueueIds}
userQueues={user?.queues}
onChange={(values) => setSelectedQueueIds(values)}
/>
</Paper>
<TabPanel value={tab} name="open" className={classes.ticketsWrapper}>
<Tabs
value={tabOpen}
onChange={handleChangeTabOpen}
indicatorColor="primary"
textColor="primary"
variant="fullWidth"
>
<Tab
label={
<Badge className={classes.badge} badgeContent={openCount} color="primary">
{i18n.t("ticketsList.assignedHeader")}
</Badge>
}
value={"open"}
/>
<Tab
label={
<Badge className={classes.badge} badgeContent={pendingCount} color="secondary">
{i18n.t("ticketsList.pendingHeader")}
</Badge>
}
value={"pending"}
/>
</Tabs>
<Paper className={classes.ticketsWrapper}>
<TicketsList
status="open"
showAll={showAllTickets}
selectedQueueIds={selectedQueueIds}
updateCount={(val) => setOpenCount(val)}
style={applyPanelStyle("open")}
/>
<TicketsList
status="pending"
selectedQueueIds={selectedQueueIds}
updateCount={(val) => setPendingCount(val)}
style={applyPanelStyle("pending")}
/>
</Paper>
</TabPanel>
<TabPanel value={tab} name="closed" className={classes.ticketsWrapper}>
<TicketsList status="closed" showAll={true} selectedQueueIds={selectedQueueIds} />
</TabPanel>
<TabPanel value={tab} name="search" className={classes.ticketsWrapper}>
<TicketsList searchParam={searchParam} showAll={true} selectedQueueIds={selectedQueueIds} />
</TabPanel>
</Paper>
);
};
export default TicketsManager;

View File

@ -9,9 +9,9 @@ import Tab from "@material-ui/core/Tab";
import Badge from "@material-ui/core/Badge"; import Badge from "@material-ui/core/Badge";
import MoveToInboxIcon from "@material-ui/icons/MoveToInbox"; import MoveToInboxIcon from "@material-ui/icons/MoveToInbox";
import CheckBoxIcon from "@material-ui/icons/CheckBox"; import CheckBoxIcon from "@material-ui/icons/CheckBox";
import FormControlLabel from "@material-ui/core/FormControlLabel"; import FormControlLabel from "@material-ui/core/FormControlLabel";
import Switch from "@material-ui/core/Switch"; import Switch from "@material-ui/core/Switch";
import { Button } from "@material-ui/core";
import NewTicketModal from "../NewTicketModal"; import NewTicketModal from "../NewTicketModal";
import TicketsList from "../TicketsList"; import TicketsList from "../TicketsList";
@ -21,7 +21,6 @@ import { i18n } from "../../translate/i18n";
import { AuthContext } from "../../context/Auth/AuthContext"; import { AuthContext } from "../../context/Auth/AuthContext";
import { Can } from "../Can"; import { Can } from "../Can";
import TicketsQueueSelect from "../TicketsQueueSelect"; import TicketsQueueSelect from "../TicketsQueueSelect";
import { Button } from "@material-ui/core";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
ticketsWrapper: { ticketsWrapper: {
@ -112,7 +111,6 @@ const TicketsManager = () => {
if (user.profile.toUpperCase() === "ADMIN") { if (user.profile.toUpperCase() === "ADMIN") {
setShowAllTickets(true); setShowAllTickets(true);
} }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
useEffect(() => { useEffect(() => {

View File

@ -6,12 +6,8 @@ import Select from "@material-ui/core/Select";
import { Checkbox, ListItemText } from "@material-ui/core"; import { Checkbox, ListItemText } from "@material-ui/core";
import { i18n } from "../../translate/i18n"; import { i18n } from "../../translate/i18n";
const TicketsQueueSelect = ({ const TicketsQueueSelect = ({ userQueues, selectedQueueIds = [], onChange }) => {
userQueues, const handleChange = (e) => {
selectedQueueIds = [],
onChange,
}) => {
const handleChange = e => {
onChange(e.target.value); onChange(e.target.value);
}; };
@ -38,7 +34,7 @@ const TicketsQueueSelect = ({
renderValue={() => i18n.t("ticketsQueueSelect.placeholder")} renderValue={() => i18n.t("ticketsQueueSelect.placeholder")}
> >
{userQueues?.length > 0 && {userQueues?.length > 0 &&
userQueues.map(queue => ( userQueues.map((queue) => (
<MenuItem dense key={queue.id} value={queue.id}> <MenuItem dense key={queue.id} value={queue.id}>
<Checkbox <Checkbox
style={{ style={{
@ -58,3 +54,4 @@ const TicketsQueueSelect = ({
}; };
export default TicketsQueueSelect; export default TicketsQueueSelect;

View File

@ -13,12 +13,35 @@ import UserModalComponent from "./UserModalImg/UserModalComponent";
import UserImg from "../../assets/images/User/user.jpg"; import UserImg from "../../assets/images/User/user.jpg";
const UserModal = ({ modal, click }) => { const UserModal = ({ modal, click, userId }) => {
const { user } = React.useContext(AuthContext); const { user } = React.useContext(AuthContext);
const InitalState = { name: user.name, email: user.email, profile: user.profile };
const [userData, setUserData] = React.useState(InitalState);
console.log(user); const InitalState = {
name: "",
email: "",
password: "",
profile: "",
};
const [userData, setUserData] = React.useState(InitalState);
const [selectedQueueIds, setSelectedQueueIds] = React.useState([]);
React.useEffect(() => {
// const fetchUser = async () => {
// if (!userId) return;
// try {
// const { data } = await api.get(`/users/${userId}`);
// setUserData((prevState) => {
// return console.log({ ...prevState, ...data });
// });
// const userQueueIds = data.queues?.map((queue) => queue.id);
// setSelectedQueueIds(userQueueIds);
// } catch (err) {
// alert(err);
// }
// };
// fetchUser();
}, [userId]);
return ( return (
<ModalOverlayStyled modal={modal}> <ModalOverlayStyled modal={modal}>
@ -30,14 +53,14 @@ const UserModal = ({ modal, click }) => {
label="Nome" label="Nome"
type="text" type="text"
value={userData.name} value={userData.name}
onChange={(event) => setUserData({name:event.target.data})} onChange={(event) => setUserData({ name: event.target.data })}
/> />
<InputComponent <InputComponent
id="email" id="email"
label="E-mail" label="E-mail"
type="email" type="email"
value={userData.email} value={userData.email}
onChange={(event) => setUserData({email:event.target.data})} onChange={(event) => setUserData({ email: event.target.data })}
/> />
<InputComponent id="password" label="Senha" type="password" /> <InputComponent id="password" label="Senha" type="password" />
<UserBtns> <UserBtns>
@ -63,3 +86,4 @@ name: "teste"
profile: "master" profile: "master"
queues: [] queues: []
tokenVersion: 0 */ tokenVersion: 0 */

View File

@ -0,0 +1,20 @@
import React from "react";
import { useParams } from "react-router-dom";
import TicketsStyled from "./Tickets.style";
import TicketsManager from "../../components/TicketsManager/TicketsManager";
import Ticket from "../../components/Ticket/";
const Tickets = () => {
const { ticketId } = useParams();
return (
<TicketsStyled>
<TicketsManager />
{ticketId ? <Ticket /> : <div>Não tem nada</div>}
</TicketsStyled>
);
};
export default Tickets;

View File

@ -0,0 +1,9 @@
import styled from "styled-components";
const TicketsStyled = styled.div`
display: flex;
flex-direction: row;
height: 86vh;
`;
export default TicketsStyled

View File

@ -10,7 +10,7 @@ import SchedulesReminder from "../pages/SchedulesReminder/";
import Login from "../pages/Login/"; import Login from "../pages/Login/";
import Signup from "../pages/Signup/"; import Signup from "../pages/Signup/";
import Tickets from "../pages/Tickets/"; import Tickets from "../pages/Tickets/Tickets";
import Connections from "../pages/Connections/"; import Connections from "../pages/Connections/";
import Settings from "../pages/Settings/"; import Settings from "../pages/Settings/";
import Users from "../pages/Users"; import Users from "../pages/Users";