Added @flare/tuitio-client-react in CDN app

master
Tudor Stanciu 2023-03-19 00:07:56 +02:00
parent 33983a785d
commit a99436e440
18 changed files with 187 additions and 290 deletions

4
.env
View File

@ -1,6 +1,6 @@
REACT_APP_IDENTITY_AUTHENTICATION_URL=https://lab.code-rove.com/identity-server-api/identity/authenticate?UserName={username}&Password={password} REACT_APP_TUITIO_URL=https://lab.code-rove.com/tuitio
REACT_APP_ENABLE_TEMPLATE_CONTENT=True REACT_APP_ENABLE_TEMPLATE_CONTENT=True
# REACT_APP_CDN_URL=http://localhost:5050 #REACT_APP_CDN_URL=http://localhost:5050
REACT_APP_CDN_URL=https://lab.code-rove.com/cdn REACT_APP_CDN_URL=https://lab.code-rove.com/cdn
#PUBLIC_URL=/cdn-admin/ #PUBLIC_URL=/cdn-admin/

View File

@ -1,4 +1,4 @@
PUBLIC_URL=/cdn-admin/ PUBLIC_URL=/cdn-admin/
REACT_APP_IDENTITY_AUTHENTICATION_URL=https://lab.code-rove.com/identity-server-api/identity/authenticate?UserName={username}&Password={password} REACT_APP_TUITIO_URL=https://lab.code-rove.com/tuitio
REACT_APP_ENABLE_TEMPLATE_CONTENT=False REACT_APP_ENABLE_TEMPLATE_CONTENT=False
REACT_APP_CDN_URL=https://lab.code-rove.com/cdn REACT_APP_CDN_URL=https://lab.code-rove.com/cdn

82
package-lock.json generated
View File

@ -1325,9 +1325,53 @@
} }
}, },
"@flare/js-utils": { "@flare/js-utils": {
"version": "1.0.2", "version": "1.0.3",
"resolved": "https://lab.code-rove.com/public-node-registry/@flare%2fjs-utils/-/js-utils-1.0.2.tgz", "resolved": "https://lab.code-rove.com/public-node-registry/@flare/js-utils/-/js-utils-1.0.3.tgz",
"integrity": "sha512-49KjpcRUD6fXPC60bmCsPa8frU6wlQyI5RsjMD70zsuvk8BZFdopsETsjsCembbmzTScIhfMACU4RnimOEwg9Q==" "integrity": "sha512-VgXQHoQEVZ/71B6YQHQP8/Yd/w1smGD+kCCiNvJKZ1xMD3nkN9mjoHxIqbOJMZ2q5PZlV6gXYT7eVol8Wm+D0A=="
},
"@flare/tuitio-client": {
"version": "1.1.0",
"resolved": "https://lab.code-rove.com/public-node-registry/@flare/tuitio-client/-/tuitio-client-1.1.0.tgz",
"integrity": "sha512-m2cYFaIdHx6eGqSo1V9Rx5sy8Zz5Y/y02OCTwuA++QceJLelUeUID+ymEts54nkG6nFxsGRBaNhpcC5lwsPMng==",
"requires": {
"@flare/js-utils": "^1.0.3",
"axios": "^1.3.2"
},
"dependencies": {
"axios": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz",
"integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==",
"requires": {
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"follow-redirects": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
},
"form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
}
}
},
"@flare/tuitio-client-react": {
"version": "1.1.1",
"resolved": "https://lab.code-rove.com/public-node-registry/@flare/tuitio-client-react/-/tuitio-client-react-1.1.1.tgz",
"integrity": "sha512-CTxc1Ra0b3RcMuHakIw5B+d/n/42N8s64avXPAWKmWfBkuOIdPJ3P9s2kpG+CV2MIBlUCPSTbfOi+Ve1pf/cig==",
"requires": {
"@flare/tuitio-client": "^1.1.0"
}
}, },
"@gar/promisify": { "@gar/promisify": {
"version": "1.1.3", "version": "1.1.3",
@ -3391,11 +3435,30 @@
"integrity": "sha512-lCZN5XRuOnpG4bpMq8v0khrWtUOn+i8lZSb6wHZH56ZfbIEv6XwJV84AAueh9/zi7qPVJ/E4yz6fmsiyOmXR4w==" "integrity": "sha512-lCZN5XRuOnpG4bpMq8v0khrWtUOn+i8lZSb6wHZH56ZfbIEv6XwJV84AAueh9/zi7qPVJ/E4yz6fmsiyOmXR4w=="
}, },
"axios": { "axios": {
"version": "0.19.2", "version": "1.3.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz",
"integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==",
"requires": { "requires": {
"follow-redirects": "1.5.10" "follow-redirects": "^1.15.0",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
},
"dependencies": {
"follow-redirects": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
},
"form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
}
} }
}, },
"axobject-query": { "axobject-query": {
@ -12806,6 +12869,11 @@
"ipaddr.js": "1.9.1" "ipaddr.js": "1.9.1"
} }
}, },
"proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"prr": { "prr": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",

View File

@ -19,7 +19,8 @@
"node-fetch": "^2.6.1" "node-fetch": "^2.6.1"
}, },
"dependencies": { "dependencies": {
"@flare/js-utils": "^1.0.2", "@flare/js-utils": "^1.0.3",
"@flare/tuitio-client-react": "^1.1.1",
"@material-ui/core": "^4.11.3", "@material-ui/core": "^4.11.3",
"@material-ui/icons": "^4.11.2", "@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.60", "@material-ui/lab": "^4.0.0-alpha.60",
@ -28,7 +29,7 @@
"@mdi/react": "^1.4.0", "@mdi/react": "^1.4.0",
"@babel/eslint-parser": "^7.16.5", "@babel/eslint-parser": "^7.16.5",
"apexcharts": "^3.24.0", "apexcharts": "^3.24.0",
"axios": "^0.19.2", "axios": "^1.3.4",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"font-awesome": "^4.7.0", "font-awesome": "^4.7.0",
"i18next": "^19.4.4", "i18next": "^19.4.4",

View File

@ -8,21 +8,10 @@ import Layout from "./Layout/Layout";
import Error from "../pages/error"; import Error from "../pages/error";
import Login from "../pages/login"; import Login from "../pages/login";
import ServerNotAvailable from "../features/server/availability/components/ServerNotAvailable"; import ServerNotAvailable from "../features/server/availability/components/ServerNotAvailable";
import { useTuitioToken } from "@flare/tuitio-client-react";
// context
import { useUserState } from "../contexts/UserContext";
import { useToast } from "../contexts/ToastContext";
import { useTranslation } from "react-i18next";
export default function App() { export default function App() {
const { authenticated, messageCode } = useUserState(); const { valid: authenticated } = useTuitioToken();
const { t } = useTranslation();
const { notify } = useToast();
useEffect(() => {
if (!messageCode) return;
notify(t(messageCode), "error");
}, [messageCode, t, notify]);
return ( return (
<BrowserRouter basename={process.env.PUBLIC_URL || ""}> <BrowserRouter basename={process.env.PUBLIC_URL || ""}>

View File

@ -33,7 +33,8 @@ import {
useLayoutDispatch, useLayoutDispatch,
toggleSidebar toggleSidebar
} from "../../contexts/LayoutContext"; } from "../../contexts/LayoutContext";
import { useUserDispatch, signOut } from "../../contexts/UserContext"; import { useTuitioClient } from "@flare/tuitio-client-react";
import { useToast } from "../../hooks";
const messages = [ const messages = [
{ {
@ -94,7 +95,6 @@ export default function Header(props) {
// global // global
var layoutState = useLayoutState(); var layoutState = useLayoutState();
var layoutDispatch = useLayoutDispatch(); var layoutDispatch = useLayoutDispatch();
var userDispatch = useUserDispatch();
// local // local
var [mailMenu, setMailMenu] = useState(null); var [mailMenu, setMailMenu] = useState(null);
@ -104,6 +104,12 @@ export default function Header(props) {
var [profileMenu, setProfileMenu] = useState(null); var [profileMenu, setProfileMenu] = useState(null);
var [isSearchOpen, setSearchOpen] = useState(false); var [isSearchOpen, setSearchOpen] = useState(false);
const { error } = useToast();
const { logout } = useTuitioClient({
onLogoutFailed: (errorMessage) => error(errorMessage),
onLogoutError: (err) => error(err.message)
});
return ( return (
<AppBar position="fixed" className={classes.appBar}> <AppBar position="fixed" className={classes.appBar}>
<Toolbar className={classes.toolbar}> <Toolbar className={classes.toolbar}>
@ -320,7 +326,7 @@ export default function Header(props) {
<Typography <Typography
className={classes.profileMenuLink} className={classes.profileMenuLink}
color="primary" color="primary"
onClick={() => signOut(userDispatch, props.history)} onClick={logout}
> >
Sign Out Sign Out
</Typography> </Typography>

View File

@ -1,7 +1,7 @@
import { makeStyles } from "@material-ui/styles"; import { makeStyles } from "@material-ui/styles";
import { fade } from "@material-ui/core/styles/colorManipulator"; import { alpha } from "@material-ui/core/styles";
export default makeStyles(theme => ({ export default makeStyles((theme) => ({
logotype: { logotype: {
color: "white", color: "white",
marginLeft: theme.spacing(2.5), marginLeft: theme.spacing(2.5),
@ -10,45 +10,45 @@ export default makeStyles(theme => ({
fontSize: 18, fontSize: 18,
whiteSpace: "nowrap", whiteSpace: "nowrap",
[theme.breakpoints.down("xs")]: { [theme.breakpoints.down("xs")]: {
display: "none", display: "none"
}, }
}, },
appBar: { appBar: {
width: "100vw", width: "100vw",
zIndex: theme.zIndex.drawer + 1, zIndex: theme.zIndex.drawer + 1,
transition: theme.transitions.create(["margin"], { transition: theme.transitions.create(["margin"], {
easing: theme.transitions.easing.sharp, easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen, duration: theme.transitions.duration.leavingScreen
}), })
}, },
toolbar: { toolbar: {
paddingLeft: theme.spacing(2), paddingLeft: theme.spacing(2),
paddingRight: theme.spacing(2), paddingRight: theme.spacing(2)
}, },
hide: { hide: {
display: "none", display: "none"
}, },
grow: { grow: {
flexGrow: 1, flexGrow: 1
}, },
search: { search: {
position: "relative", position: "relative",
borderRadius: 25, borderRadius: 25,
paddingLeft: theme.spacing(2.5), paddingLeft: theme.spacing(2.5),
width: 36, width: 36,
backgroundColor: fade(theme.palette.common.black, 0), backgroundColor: alpha(theme.palette.common.black, 0),
transition: theme.transitions.create(["background-color", "width"]), transition: theme.transitions.create(["background-color", "width"]),
"&:hover": { "&:hover": {
cursor: "pointer", cursor: "pointer",
backgroundColor: fade(theme.palette.common.black, 0.08), backgroundColor: alpha(theme.palette.common.black, 0.08)
}, }
}, },
searchFocused: { searchFocused: {
backgroundColor: fade(theme.palette.common.black, 0.08), backgroundColor: alpha(theme.palette.common.black, 0.08),
width: "100%", width: "100%",
[theme.breakpoints.up("md")]: { [theme.breakpoints.up("md")]: {
width: 250, width: 250
}, }
}, },
searchIcon: { searchIcon: {
width: 36, width: 36,
@ -60,115 +60,115 @@ export default makeStyles(theme => ({
justifyContent: "center", justifyContent: "center",
transition: theme.transitions.create("right"), transition: theme.transitions.create("right"),
"&:hover": { "&:hover": {
cursor: "pointer", cursor: "pointer"
}, }
}, },
searchIconOpened: { searchIconOpened: {
right: theme.spacing(1.25), right: theme.spacing(1.25)
}, },
inputRoot: { inputRoot: {
color: "inherit", color: "inherit",
width: "100%", width: "100%"
}, },
inputInput: { inputInput: {
height: 36, height: 36,
padding: 0, padding: 0,
paddingRight: 36 + theme.spacing(1.25), paddingRight: 36 + theme.spacing(1.25),
width: "100%", width: "100%"
}, },
messageContent: { messageContent: {
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column"
}, },
headerMenu: { headerMenu: {
marginTop: theme.spacing(7), marginTop: theme.spacing(7)
}, },
headerMenuList: { headerMenuList: {
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column"
}, },
headerMenuItem: { headerMenuItem: {
"&:hover, &:focus": { "&:hover, &:focus": {
backgroundColor: theme.palette.background.light, backgroundColor: theme.palette.background.light
// color: "white", // color: "white",
}, }
}, },
headerMenuButton: { headerMenuButton: {
marginLeft: theme.spacing(2), marginLeft: theme.spacing(2),
padding: theme.spacing(0.5), padding: theme.spacing(0.5)
}, },
headerMenuButtonSandwich: { headerMenuButtonSandwich: {
marginLeft: 9, marginLeft: 9,
[theme.breakpoints.down("sm")]: { [theme.breakpoints.down("sm")]: {
marginLeft: 0 marginLeft: 0
}, },
padding: theme.spacing(0.5), padding: theme.spacing(0.5)
}, },
headerMenuButtonCollapse: { headerMenuButtonCollapse: {
marginRight: theme.spacing(2), marginRight: theme.spacing(2)
}, },
headerIcon: { headerIcon: {
fontSize: 28, fontSize: 28,
color: "rgba(255, 255, 255, 0.35)", color: "rgba(255, 255, 255, 0.35)"
}, },
headerIconCollapse: { headerIconCollapse: {
color: "white", color: "white"
}, },
profileMenu: { profileMenu: {
minWidth: 265, minWidth: 265
}, },
profileMenuUser: { profileMenuUser: {
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
padding: theme.spacing(2), padding: theme.spacing(2)
}, },
profileMenuItem: { profileMenuItem: {
color: theme.palette.text.hint, color: theme.palette.text.hint
}, },
profileMenuIcon: { profileMenuIcon: {
marginRight: theme.spacing(2), marginRight: theme.spacing(2),
color: theme.palette.text.hint, color: theme.palette.text.hint,
'&:hover': { "&:hover": {
color: theme.palette.primary.main, color: theme.palette.primary.main
} }
}, },
profileMenuLink: { profileMenuLink: {
fontSize: 16, fontSize: 16,
textDecoration: "none", textDecoration: "none",
"&:hover": { "&:hover": {
cursor: "pointer", cursor: "pointer"
}, }
}, },
messageNotification: { messageNotification: {
height: "auto", height: "auto",
display: "flex", display: "flex",
alignItems: "center", alignItems: "center",
"&:hover, &:focus": { "&:hover, &:focus": {
backgroundColor: theme.palette.background.light, backgroundColor: theme.palette.background.light
}, }
}, },
messageNotificationSide: { messageNotificationSide: {
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
alignItems: "center", alignItems: "center",
marginRight: theme.spacing(2), marginRight: theme.spacing(2)
}, },
messageNotificationBodySide: { messageNotificationBodySide: {
alignItems: "flex-start", alignItems: "flex-start",
marginRight: 0, marginRight: 0
}, },
sendMessageButton: { sendMessageButton: {
margin: theme.spacing(4), margin: theme.spacing(4),
marginTop: theme.spacing(2), marginTop: theme.spacing(2),
marginBottom: theme.spacing(2), marginBottom: theme.spacing(2),
textTransform: "none", textTransform: "none"
}, },
sendButtonIcon: { sendButtonIcon: {
marginLeft: theme.spacing(2), marginLeft: theme.spacing(2)
}, },
purchaseBtn: { purchaseBtn: {
[theme.breakpoints.down('sm')]: { [theme.breakpoints.down("sm")]: {
display: 'none' display: "none"
}, },
marginRight: theme.spacing(3) marginRight: theme.spacing(3)
} }

View File

@ -61,7 +61,7 @@ export default function Widget({
aria-owns="widget-menu" aria-owns="widget-menu"
aria-haspopup="true" aria-haspopup="true"
onClick={() => setMoreMenuOpen(true)} onClick={() => setMoreMenuOpen(true)}
buttonRef={setMoreButtonRef} ref={setMoreButtonRef}
> >
<MoreIcon /> <MoreIcon />
</IconButton> </IconButton>

View File

@ -37,7 +37,7 @@ const Widget = ({
aria-owns="widget-menu" aria-owns="widget-menu"
aria-haspopup="true" aria-haspopup="true"
onClick={() => props.setMoreMenuOpen(true)} onClick={() => props.setMoreMenuOpen(true)}
buttonRef={props.setMoreButtonRef} ref={props.setMoreButtonRef}
> >
<MoreIcon /> <MoreIcon />
</IconButton> </IconButton>
@ -77,7 +77,7 @@ const Widget = ({
</div> </div>
); );
const styles = theme => ({ const styles = (theme) => ({
widgetWrapper: { widgetWrapper: {
display: "flex", display: "flex",
minHeight: "100%" minHeight: "100%"

View File

@ -1,100 +0,0 @@
import React from "react";
import PropTypes from "prop-types";
import { authenticate, invalidate, validateToken } from "../utils/identity";
var UserStateContext = React.createContext();
var UserDispatchContext = React.createContext();
function userReducer(state, action) {
switch (action.type) {
case "LOGIN_SUCCESS":
return { ...state, ...action.payload, authenticated: true };
case "SIGN_OUT_SUCCESS":
return { ...state, authenticated: false };
case "LOGIN_FAILURE":
return {
...state,
authenticated: false,
messageCode: action.payload?.messageCode
};
default: {
throw new Error(`Unhandled action type: ${action.type}`);
}
}
}
function UserProvider({ children }) {
const { valid, token } = validateToken();
var [state, dispatch] = React.useReducer(userReducer, {
authenticated: valid === true,
token
});
return (
<UserStateContext.Provider value={state}>
<UserDispatchContext.Provider value={dispatch}>
{children}
</UserDispatchContext.Provider>
</UserStateContext.Provider>
);
}
UserProvider.propTypes = {
children: PropTypes.node
};
function useUserState() {
var context = React.useContext(UserStateContext);
if (context === undefined) {
throw new Error("useUserState must be used within a UserProvider");
}
return context;
}
function useUserDispatch() {
var context = React.useContext(UserDispatchContext);
if (context === undefined) {
throw new Error("useUserDispatch must be used within a UserProvider");
}
return context;
}
function setLoginFailed(dispatch, setError, setIsLoading, messageCode) {
dispatch({ type: "LOGIN_FAILURE", payload: { messageCode } });
setError(true);
setIsLoading(false);
}
function loginUser(dispatch, login, password, history, setIsLoading, setError) {
setError(false);
setIsLoading(true);
if (!!login && !!password) {
authenticate(login, password).then((response) => {
if (response.status === "SUCCESS") {
setError(null);
setIsLoading(false);
dispatch({ type: "LOGIN_SUCCESS", payload: { token: response.token } });
} else if (response.status === "BAD_CREDENTIALS") {
setLoginFailed(
dispatch,
setError,
setIsLoading,
"Login.IncorrectCredentials"
);
}
});
history.push("/dashboard");
} else {
setLoginFailed(dispatch, setError, setIsLoading, "Login.EmptyCredentials");
}
}
function signOut(dispatch, history) {
invalidate();
dispatch({ type: "SIGN_OUT_SUCCESS" });
history.push("/login");
}
export { UserProvider, useUserState, useUserDispatch, loginUser, signOut };

View File

@ -136,7 +136,7 @@ const ResourceComponent = ({
<TextField <TextField
fullWidth fullWidth
multiline multiline
rows={2} minRows={2}
id="resource-description" id="resource-description"
label={t("Resource.Description")} label={t("Resource.Description")}
variant="outlined" variant="outlined"

View File

@ -6,7 +6,6 @@ import { LoadingText } from "../../../../components";
import ResourceComponent from "./ResourceComponent"; import ResourceComponent from "./ResourceComponent";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import style from "../styles"; import style from "../styles";
import { useToast } from "../../../../hooks";
const useStyles = makeStyles(style); const useStyles = makeStyles(style);
@ -21,7 +20,6 @@ const ResourceContainer = () => {
const params = useParams(); const params = useParams();
const { getResource } = useResourcesApi(); const { getResource } = useResourcesApi();
const { getMimeTypes, getResourceCategories } = useDictionariesApi(); const { getMimeTypes, getResourceCategories } = useDictionariesApi();
const { success } = useToast();
const isNew = useMemo(() => params.id === "new", [params.id]); const isNew = useMemo(() => params.id === "new", [params.id]);

View File

@ -1,15 +1,15 @@
import { useUserState } from "../contexts/UserContext"; import { useTuitioToken } from "@flare/tuitio-client-react";
import { useCallback } from "react"; import { useCallback } from "react";
const useResourceSecurity = () => { const useResourceSecurity = () => {
const { token } = useUserState(); const { token } = useTuitioToken();
const secureUrl = useCallback( const secureUrl = useCallback(
(url) => { (url) => {
const separator = url.includes("?") ? "&" : "?"; const separator = url.includes("?") ? "&" : "?";
const securedUrl = `${url}${separator}token=${token.raw}`; const securedUrl = `${url}${separator}token=${token}`;
return securedUrl; return securedUrl;
}, },
[token.raw] [token]
); );
return { secureUrl }; return { secureUrl };

View File

@ -1,18 +1,18 @@
import React, { Suspense } from "react"; import React, { Suspense } from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import { TuitioProvider } from "@flare/tuitio-client-react";
import { ThemeProvider } from "@material-ui/styles"; import { ThemeProvider } from "@material-ui/styles";
import { CssBaseline } from "@material-ui/core"; import { CssBaseline } from "@material-ui/core";
import Themes from "./themes"; import Themes from "./themes";
import App from "./components/App"; import App from "./components/App";
import * as serviceWorker from "./serviceWorker"; import * as serviceWorker from "./serviceWorker";
import { LayoutProvider } from "./contexts/LayoutContext"; import { LayoutProvider } from "./contexts/LayoutContext";
import { UserProvider } from "./contexts/UserContext";
import { ToastProvider } from "./contexts/ToastContext"; import { ToastProvider } from "./contexts/ToastContext";
import "./utils/i18n"; import "./utils/i18n";
ReactDOM.render( ReactDOM.render(
<LayoutProvider> <LayoutProvider>
<UserProvider> <TuitioProvider tuitioUrl={process.env.REACT_APP_TUITIO_URL}>
<ThemeProvider theme={Themes.default}> <ThemeProvider theme={Themes.default}>
<CssBaseline /> <CssBaseline />
<Suspense fallback={<div>Loading...</div>}> <Suspense fallback={<div>Loading...</div>}>
@ -21,7 +21,7 @@ ReactDOM.render(
</ToastProvider> </ToastProvider>
</Suspense> </Suspense>
</ThemeProvider> </ThemeProvider>
</UserProvider> </TuitioProvider>
</LayoutProvider>, </LayoutProvider>,
document.getElementById("root") document.getElementById("root")
); );

View File

@ -18,23 +18,32 @@ import useStyles from "./styles";
// logo // logo
import logo from "./logo.svg"; import logo from "./logo.svg";
import google from "../../images/google.svg"; import google from "../../images/google.svg";
import { useToast } from "../../hooks";
import { useTuitioClient } from "@flare/tuitio-client-react";
// context function Login() {
import { useUserDispatch, loginUser } from "../../contexts/UserContext"; const classes = useStyles();
const [activeTabId, setActiveTabId] = useState(0);
const [nameValue, setNameValue] = useState("");
const [loginValue, setLoginValue] = useState("");
const [passwordValue, setPasswordValue] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [errorEncountered, setErrorEncountered] = useState(false);
function Login(props) { const { error } = useToast();
var classes = useStyles(); const { login } = useTuitioClient({
onLoginSuccess: () => setIsLoading(false),
onLoginFailed: () => error(t("Login.IncorrectCredentials")),
onLoginError: (err) => {
setErrorEncountered(true);
error(err.message);
}
});
// global const handleLogin = () => {
var userDispatch = useUserDispatch(); setIsLoading(true);
login(loginValue, passwordValue);
// local };
var [isLoading, setIsLoading] = useState(false);
var [error, setError] = useState(null);
var [activeTabId, setActiveTabId] = useState(0);
var [nameValue, setNameValue] = useState("");
var [loginValue, setLoginValue] = useState("");
var [passwordValue, setPasswordValue] = useState("");
return ( return (
<Grid container className={classes.container}> <Grid container className={classes.container}>
@ -68,7 +77,7 @@ function Login(props) {
<Typography className={classes.formDividerWord}>or</Typography> <Typography className={classes.formDividerWord}>or</Typography>
<div className={classes.formDivider} /> <div className={classes.formDivider} />
</div> </div>
<Fade in={error}> <Fade in={errorEncountered}>
<Typography color="secondary" className={classes.errorMessage}> <Typography color="secondary" className={classes.errorMessage}>
Something is wrong with your login or password :( Something is wrong with your login or password :(
</Typography> </Typography>
@ -111,16 +120,7 @@ function Login(props) {
disabled={ disabled={
loginValue.length === 0 || passwordValue.length === 0 loginValue.length === 0 || passwordValue.length === 0
} }
onClick={() => onClick={handleLogin}
loginUser(
userDispatch,
loginValue,
passwordValue,
props.history,
setIsLoading,
setError
)
}
variant="contained" variant="contained"
color="primary" color="primary"
size="large" size="large"
@ -201,16 +201,7 @@ function Login(props) {
<CircularProgress size={26} /> <CircularProgress size={26} />
) : ( ) : (
<Button <Button
onClick={() => onClick={handleLogin}
loginUser(
userDispatch,
loginValue,
passwordValue,
props.history,
setIsLoading,
setError
)
}
disabled={ disabled={
loginValue.length === 0 || loginValue.length === 0 ||
passwordValue.length === 0 || passwordValue.length === 0 ||

View File

@ -1,32 +1,31 @@
import defaultTheme from "./default"; import defaultTheme from "./default";
import { createTheme } from "@material-ui/core/styles";
import { createMuiTheme } from "@material-ui/core";
const overrides = { const overrides = {
typography: { typography: {
h1: { h1: {
fontSize: "3rem", fontSize: "3rem"
}, },
h2: { h2: {
fontSize: "2rem", fontSize: "2rem"
}, },
h3: { h3: {
fontSize: "1.64rem", fontSize: "1.64rem"
}, },
h4: { h4: {
fontSize: "1.5rem", fontSize: "1.5rem"
}, },
h5: { h5: {
fontSize: "1.285rem", fontSize: "1.285rem"
}, },
h6: { h6: {
fontSize: "1.142rem", fontSize: "1.142rem"
}, }
}, }
}; };
const themes = { const themes = {
default: createMuiTheme({ ...defaultTheme, ...overrides }), default: createTheme({ ...defaultTheme, ...overrides })
}; };
export default themes; export default themes;

View File

@ -1,17 +1,14 @@
import axios from "axios"; import axios from "axios";
import i18next from "i18next"; import i18next from "i18next";
import { storageKeys } from "./identity"; import { fetch as fetchTuitioData } from "@flare/tuitio-client";
import { localStorage } from "@flare/js-utils";
const { getItem } = localStorage;
function getHeaders() { function getHeaders() {
const token = getItem(storageKeys.TOKEN); const { token } = fetchTuitioData();
const language = i18next.language; const language = i18next.language;
return { return {
"Content-Type": "application/json", "Content-Type": "application/json",
Authorization: `Basic ${token.raw}`, Authorization: `Tuitio ${token}`,
"Accept-Language": `${language}` "Accept-Language": `${language}`
}; };
} }

View File

@ -1,52 +0,0 @@
import { request } from "./axios";
import { localStorage } from "@flare/js-utils";
const { setItem, getItem, removeItem } = localStorage;
const storageKeys = {
TOKEN: "AUTHORIZATION_TOKEN",
USER: "USER_NAME"
};
const authenticate = async (userName, password) => {
const urlTemplate = process.env.REACT_APP_IDENTITY_AUTHENTICATION_URL;
const url = urlTemplate
.replace("{username}", userName)
.replace("{password}", password);
const options = {
method: "post"
};
const response = await request(url, options);
if (response.status === "SUCCESS") {
setItem(storageKeys.TOKEN, response.token);
setItem(storageKeys.USER, userName);
}
return response;
};
const invalidate = () => {
const token = getItem(storageKeys.TOKEN);
if (token) {
removeItem(storageKeys.TOKEN);
removeItem(storageKeys.USER);
}
};
const validateToken = () => {
let token = getItem(storageKeys.TOKEN);
if (!token) {
return { valid: false };
}
const valid = new Date(token.validUntil) >= new Date();
if (!valid) {
invalidate();
token = null;
}
return { valid, token };
};
export { storageKeys, authenticate, invalidate, validateToken };