Merged PR 76: Added @flare/tuitio-client-react in CDN app
Added @flare/tuitio-client-react in CDN appmaster
commit
edb5f41a78
4
.env
4
.env
|
@ -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/
|
|
@ -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
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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 || ""}>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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%"
|
||||||
|
|
|
@ -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 };
|
|
|
@ -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"
|
||||||
|
|
|
@ -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]);
|
||||||
|
|
||||||
|
|
|
@ -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 };
|
||||||
|
|
|
@ -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")
|
||||||
);
|
);
|
||||||
|
|
|
@ -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 ||
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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}`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 };
|
|
Loading…
Reference in New Issue