Compare commits
8 Commits
c83bb6aec6
...
96c3cf8fbc
Author | SHA1 | Date |
---|---|---|
Tudor Stanciu | 96c3cf8fbc | |
Tudor Stanciu | edb5f41a78 | |
Tudor Stanciu | a99436e440 | |
Tudor Stanciu | 33983a785d | |
Tudor Stanciu | e7fa2ea573 | |
Tudor Stanciu | 32a05db318 | |
Tudor Stanciu | 08dd469be3 | |
Tudor Stanciu | 6039dda5ce |
5
.env
5
.env
|
@ -1,5 +1,6 @@
|
|||
REACT_APP_IDENTITY_AUTHENTICATION_URL=https://toodle.ddns.net/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_CDN_URL=http://localhost:5050
|
||||
#REACT_APP_CDN_URL=http://localhost:5050
|
||||
REACT_APP_CDN_URL=https://lab.code-rove.com/cdn
|
||||
|
||||
#PUBLIC_URL=/cdn-admin/
|
|
@ -1,4 +1,4 @@
|
|||
PUBLIC_URL=/cdn-admin/
|
||||
REACT_APP_IDENTITY_AUTHENTICATION_URL=https://toodle.ddns.net/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_CDN_URL=https://toodle.ddns.net/cdn
|
||||
REACT_APP_CDN_URL=https://lab.code-rove.com/cdn
|
2
.npmrc
2
.npmrc
|
@ -1 +1 @@
|
|||
@flare:registry=https://toodle.ddns.net/public-node-registry
|
||||
@flare:registry=https://lab.code-rove.com/public-node-registry
|
|
@ -4,7 +4,7 @@ Built with [React](https://facebook.github.io/react/), [Material-UI](https://mat
|
|||
|
||||
**This version uses React 16.14.0, React Router v5, MaterialUI v4, built with React Hooks and React Context (No Redux)**
|
||||
|
||||
[View Demo](https://toodle.ddns.net/cdn-admin/) | [Download](https://toodle.ddns.net/gitea/tudor/cdn-frontend) | [More projects](https://toodle.ddns.net/heimdall) | [Support forum](https://toodle.ddns.net/forum)
|
||||
[View Demo](https://lab.code-rove.com/cdn-admin/) | [Download](https://lab.code-rove.com/gitea/tudor/cdn-frontend) | [More projects](https://lab.code-rove.com/heimdall) | [Support forum](https://lab.code-rove.com/forum)
|
||||
|
||||
[![image](https://user-images.githubusercontent.com/24964748/55800639-df780300-5adc-11e9-84b7-7c2437088516.png)](https://flatlogic.com/admin-dashboards/react-material-admin/demo)
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# build environment
|
||||
FROM node:12-slim as builder
|
||||
FROM node:14-slim as builder
|
||||
WORKDIR /app
|
||||
|
||||
COPY .npmrc .npmrc
|
||||
|
@ -11,7 +11,7 @@ COPY . ./
|
|||
RUN npm run build
|
||||
|
||||
# production environment
|
||||
FROM node:12-slim
|
||||
FROM node:14-slim
|
||||
RUN printf '\n\n- Copy application files\n'
|
||||
|
||||
ARG APP_SUBFOLDER=cdn-admin
|
||||
|
@ -22,6 +22,11 @@ COPY --from=builder /app/build/index.html ./application/
|
|||
#install static server
|
||||
RUN npm install -g serve
|
||||
|
||||
# environment variables
|
||||
ENV AUTHOR="Tudor Stanciu"
|
||||
ARG APP_VERSION=0.0.0
|
||||
ENV APP_VERSION=${APP_VERSION}
|
||||
|
||||
#set workdir to root
|
||||
WORKDIR /
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
|
@ -1,15 +1,15 @@
|
|||
{
|
||||
"name": "cdn-admin",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0",
|
||||
"description": "CDN administration application",
|
||||
"author": {
|
||||
"name": "Tudor Stanciu",
|
||||
"email": "tudor.stanciu94@gmail.com",
|
||||
"url": "https://toodle.ddns.net/tsp"
|
||||
"url": "https://lab.code-rove.com/tsp"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://toodle.ddns.net/gitea/tudor/cdn-frontend"
|
||||
"url": "https://lab.code-rove.com/gitea/tudor/cdn-frontend"
|
||||
},
|
||||
"private": true,
|
||||
"homepage": "./",
|
||||
|
@ -19,7 +19,8 @@
|
|||
"node-fetch": "^2.6.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@flare/js-utils": "^1.0.0",
|
||||
"@flare/js-utils": "^1.0.3",
|
||||
"@flare/tuitio-client-react": "^1.1.1",
|
||||
"@material-ui/core": "^4.11.3",
|
||||
"@material-ui/icons": "^4.11.2",
|
||||
"@material-ui/lab": "^4.0.0-alpha.60",
|
||||
|
@ -28,7 +29,7 @@
|
|||
"@mdi/react": "^1.4.0",
|
||||
"@babel/eslint-parser": "^7.16.5",
|
||||
"apexcharts": "^3.24.0",
|
||||
"axios": "^0.19.2",
|
||||
"axios": "^1.3.4",
|
||||
"classnames": "^2.2.6",
|
||||
"font-awesome": "^4.7.0",
|
||||
"i18next": "^19.4.4",
|
||||
|
|
|
@ -8,21 +8,10 @@ import Layout from "./Layout/Layout";
|
|||
import Error from "../pages/error";
|
||||
import Login from "../pages/login";
|
||||
import ServerNotAvailable from "../features/server/availability/components/ServerNotAvailable";
|
||||
|
||||
// context
|
||||
import { useUserState } from "../contexts/UserContext";
|
||||
import { useToast } from "../contexts/ToastContext";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useTuitioToken } from "@flare/tuitio-client-react";
|
||||
|
||||
export default function App() {
|
||||
const { authenticated, messageCode } = useUserState();
|
||||
const { t } = useTranslation();
|
||||
const { notify } = useToast();
|
||||
|
||||
useEffect(() => {
|
||||
if (!messageCode) return;
|
||||
notify(t(messageCode), "error");
|
||||
}, [messageCode, t, notify]);
|
||||
const { valid: authenticated } = useTuitioToken();
|
||||
|
||||
return (
|
||||
<BrowserRouter basename={process.env.PUBLIC_URL || ""}>
|
||||
|
|
|
@ -33,7 +33,8 @@ import {
|
|||
useLayoutDispatch,
|
||||
toggleSidebar
|
||||
} from "../../contexts/LayoutContext";
|
||||
import { useUserDispatch, signOut } from "../../contexts/UserContext";
|
||||
import { useTuitioClient } from "@flare/tuitio-client-react";
|
||||
import { useToast } from "../../hooks";
|
||||
|
||||
const messages = [
|
||||
{
|
||||
|
@ -94,7 +95,6 @@ export default function Header(props) {
|
|||
// global
|
||||
var layoutState = useLayoutState();
|
||||
var layoutDispatch = useLayoutDispatch();
|
||||
var userDispatch = useUserDispatch();
|
||||
|
||||
// local
|
||||
var [mailMenu, setMailMenu] = useState(null);
|
||||
|
@ -104,6 +104,12 @@ export default function Header(props) {
|
|||
var [profileMenu, setProfileMenu] = useState(null);
|
||||
var [isSearchOpen, setSearchOpen] = useState(false);
|
||||
|
||||
const { error } = useToast();
|
||||
const { logout } = useTuitioClient({
|
||||
onLogoutFailed: (errorMessage) => error(errorMessage),
|
||||
onLogoutError: (err) => error(err.message)
|
||||
});
|
||||
|
||||
return (
|
||||
<AppBar position="fixed" className={classes.appBar}>
|
||||
<Toolbar className={classes.toolbar}>
|
||||
|
@ -320,7 +326,7 @@ export default function Header(props) {
|
|||
<Typography
|
||||
className={classes.profileMenuLink}
|
||||
color="primary"
|
||||
onClick={() => signOut(userDispatch, props.history)}
|
||||
onClick={logout}
|
||||
>
|
||||
Sign Out
|
||||
</Typography>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
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: {
|
||||
color: "white",
|
||||
marginLeft: theme.spacing(2.5),
|
||||
|
@ -10,45 +10,45 @@ export default makeStyles(theme => ({
|
|||
fontSize: 18,
|
||||
whiteSpace: "nowrap",
|
||||
[theme.breakpoints.down("xs")]: {
|
||||
display: "none",
|
||||
},
|
||||
display: "none"
|
||||
}
|
||||
},
|
||||
appBar: {
|
||||
width: "100vw",
|
||||
zIndex: theme.zIndex.drawer + 1,
|
||||
transition: theme.transitions.create(["margin"], {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.leavingScreen,
|
||||
}),
|
||||
duration: theme.transitions.duration.leavingScreen
|
||||
})
|
||||
},
|
||||
toolbar: {
|
||||
paddingLeft: theme.spacing(2),
|
||||
paddingRight: theme.spacing(2),
|
||||
paddingRight: theme.spacing(2)
|
||||
},
|
||||
hide: {
|
||||
display: "none",
|
||||
display: "none"
|
||||
},
|
||||
grow: {
|
||||
flexGrow: 1,
|
||||
flexGrow: 1
|
||||
},
|
||||
search: {
|
||||
position: "relative",
|
||||
borderRadius: 25,
|
||||
paddingLeft: theme.spacing(2.5),
|
||||
width: 36,
|
||||
backgroundColor: fade(theme.palette.common.black, 0),
|
||||
backgroundColor: alpha(theme.palette.common.black, 0),
|
||||
transition: theme.transitions.create(["background-color", "width"]),
|
||||
"&:hover": {
|
||||
cursor: "pointer",
|
||||
backgroundColor: fade(theme.palette.common.black, 0.08),
|
||||
},
|
||||
backgroundColor: alpha(theme.palette.common.black, 0.08)
|
||||
}
|
||||
},
|
||||
searchFocused: {
|
||||
backgroundColor: fade(theme.palette.common.black, 0.08),
|
||||
backgroundColor: alpha(theme.palette.common.black, 0.08),
|
||||
width: "100%",
|
||||
[theme.breakpoints.up("md")]: {
|
||||
width: 250,
|
||||
},
|
||||
width: 250
|
||||
}
|
||||
},
|
||||
searchIcon: {
|
||||
width: 36,
|
||||
|
@ -60,115 +60,115 @@ export default makeStyles(theme => ({
|
|||
justifyContent: "center",
|
||||
transition: theme.transitions.create("right"),
|
||||
"&:hover": {
|
||||
cursor: "pointer",
|
||||
},
|
||||
cursor: "pointer"
|
||||
}
|
||||
},
|
||||
searchIconOpened: {
|
||||
right: theme.spacing(1.25),
|
||||
right: theme.spacing(1.25)
|
||||
},
|
||||
inputRoot: {
|
||||
color: "inherit",
|
||||
width: "100%",
|
||||
width: "100%"
|
||||
},
|
||||
inputInput: {
|
||||
height: 36,
|
||||
padding: 0,
|
||||
paddingRight: 36 + theme.spacing(1.25),
|
||||
width: "100%",
|
||||
width: "100%"
|
||||
},
|
||||
messageContent: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
flexDirection: "column"
|
||||
},
|
||||
headerMenu: {
|
||||
marginTop: theme.spacing(7),
|
||||
marginTop: theme.spacing(7)
|
||||
},
|
||||
headerMenuList: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
flexDirection: "column"
|
||||
},
|
||||
headerMenuItem: {
|
||||
"&:hover, &:focus": {
|
||||
backgroundColor: theme.palette.background.light,
|
||||
backgroundColor: theme.palette.background.light
|
||||
// color: "white",
|
||||
},
|
||||
}
|
||||
},
|
||||
headerMenuButton: {
|
||||
marginLeft: theme.spacing(2),
|
||||
padding: theme.spacing(0.5),
|
||||
padding: theme.spacing(0.5)
|
||||
},
|
||||
headerMenuButtonSandwich: {
|
||||
marginLeft: 9,
|
||||
[theme.breakpoints.down("sm")]: {
|
||||
marginLeft: 0
|
||||
},
|
||||
padding: theme.spacing(0.5),
|
||||
padding: theme.spacing(0.5)
|
||||
},
|
||||
headerMenuButtonCollapse: {
|
||||
marginRight: theme.spacing(2),
|
||||
marginRight: theme.spacing(2)
|
||||
},
|
||||
headerIcon: {
|
||||
fontSize: 28,
|
||||
color: "rgba(255, 255, 255, 0.35)",
|
||||
color: "rgba(255, 255, 255, 0.35)"
|
||||
},
|
||||
headerIconCollapse: {
|
||||
color: "white",
|
||||
color: "white"
|
||||
},
|
||||
profileMenu: {
|
||||
minWidth: 265,
|
||||
minWidth: 265
|
||||
},
|
||||
profileMenuUser: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
padding: theme.spacing(2),
|
||||
padding: theme.spacing(2)
|
||||
},
|
||||
profileMenuItem: {
|
||||
color: theme.palette.text.hint,
|
||||
color: theme.palette.text.hint
|
||||
},
|
||||
profileMenuIcon: {
|
||||
marginRight: theme.spacing(2),
|
||||
color: theme.palette.text.hint,
|
||||
'&:hover': {
|
||||
color: theme.palette.primary.main,
|
||||
"&:hover": {
|
||||
color: theme.palette.primary.main
|
||||
}
|
||||
},
|
||||
profileMenuLink: {
|
||||
fontSize: 16,
|
||||
textDecoration: "none",
|
||||
"&:hover": {
|
||||
cursor: "pointer",
|
||||
},
|
||||
cursor: "pointer"
|
||||
}
|
||||
},
|
||||
messageNotification: {
|
||||
height: "auto",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
"&:hover, &:focus": {
|
||||
backgroundColor: theme.palette.background.light,
|
||||
},
|
||||
backgroundColor: theme.palette.background.light
|
||||
}
|
||||
},
|
||||
messageNotificationSide: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
marginRight: theme.spacing(2),
|
||||
marginRight: theme.spacing(2)
|
||||
},
|
||||
messageNotificationBodySide: {
|
||||
alignItems: "flex-start",
|
||||
marginRight: 0,
|
||||
marginRight: 0
|
||||
},
|
||||
sendMessageButton: {
|
||||
margin: theme.spacing(4),
|
||||
marginTop: theme.spacing(2),
|
||||
marginBottom: theme.spacing(2),
|
||||
textTransform: "none",
|
||||
textTransform: "none"
|
||||
},
|
||||
sendButtonIcon: {
|
||||
marginLeft: theme.spacing(2),
|
||||
marginLeft: theme.spacing(2)
|
||||
},
|
||||
purchaseBtn: {
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
display: 'none'
|
||||
[theme.breakpoints.down("sm")]: {
|
||||
display: "none"
|
||||
},
|
||||
marginRight: theme.spacing(3)
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ const ContentFooter = () => {
|
|||
<div>
|
||||
<Link
|
||||
color={"primary"}
|
||||
href={"https://toodle.ddns.net/heimdall"}
|
||||
href={"https://lab.code-rove.com/heimdall"}
|
||||
target={"_blank"}
|
||||
className={classes.link}
|
||||
>
|
||||
|
@ -34,7 +34,7 @@ const ContentFooter = () => {
|
|||
</Link>
|
||||
<Link
|
||||
color={"primary"}
|
||||
href={"https://toodle.ddns.net/heimdall"}
|
||||
href={"https://lab.code-rove.com/heimdall"}
|
||||
target={"_blank"}
|
||||
className={classes.link}
|
||||
>
|
||||
|
@ -42,7 +42,7 @@ const ContentFooter = () => {
|
|||
</Link>
|
||||
<Link
|
||||
color={"primary"}
|
||||
href={"https://toodle.ddns.net/heimdall"}
|
||||
href={"https://lab.code-rove.com/heimdall"}
|
||||
target={"_blank"}
|
||||
className={classes.link}
|
||||
>
|
||||
|
@ -60,7 +60,7 @@ const ContentFooter = () => {
|
|||
<Icon path={TwitterIcon} size={1} color="#6E6E6E99" />
|
||||
</IconButton>
|
||||
</Link>
|
||||
<Link href={"https://toodle.ddns.net/gitea/"} target={"_blank"}>
|
||||
<Link href={"https://lab.code-rove.com/gitea/"} target={"_blank"}>
|
||||
<IconButton aria-label="github" style={{ marginRight: -12 }}>
|
||||
<Icon path={GithubIcon} size={1} color="#6E6E6E99" />
|
||||
</IconButton>
|
||||
|
|
|
@ -68,19 +68,19 @@ let menu = [
|
|||
{
|
||||
id: 9,
|
||||
label: "Menu.Library",
|
||||
link: "https://toodle.ddns.net/gitea/tudor/cdn-frontend",
|
||||
link: "https://lab.code-rove.com/gitea/tudor/cdn-frontend",
|
||||
icon: <LibraryIcon />
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
label: "Menu.Support",
|
||||
link: "https://toodle.ddns.net/tsp",
|
||||
link: "https://lab.code-rove.com/tsp",
|
||||
icon: <SupportIcon />
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
label: "Menu.FAQ",
|
||||
link: "https://toodle.ddns.net/tsp",
|
||||
link: "https://lab.code-rove.com/tsp",
|
||||
icon: <FAQIcon />
|
||||
}
|
||||
];
|
||||
|
|
|
@ -61,7 +61,7 @@ export default function Widget({
|
|||
aria-owns="widget-menu"
|
||||
aria-haspopup="true"
|
||||
onClick={() => setMoreMenuOpen(true)}
|
||||
buttonRef={setMoreButtonRef}
|
||||
ref={setMoreButtonRef}
|
||||
>
|
||||
<MoreIcon />
|
||||
</IconButton>
|
||||
|
|
|
@ -37,7 +37,7 @@ const Widget = ({
|
|||
aria-owns="widget-menu"
|
||||
aria-haspopup="true"
|
||||
onClick={() => props.setMoreMenuOpen(true)}
|
||||
buttonRef={props.setMoreButtonRef}
|
||||
ref={props.setMoreButtonRef}
|
||||
>
|
||||
<MoreIcon />
|
||||
</IconButton>
|
||||
|
@ -77,7 +77,7 @@ const Widget = ({
|
|||
</div>
|
||||
);
|
||||
|
||||
const styles = theme => ({
|
||||
const styles = (theme) => ({
|
||||
widgetWrapper: {
|
||||
display: "flex",
|
||||
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
|
||||
fullWidth
|
||||
multiline
|
||||
rows={12}
|
||||
minRows={2}
|
||||
id="resource-description"
|
||||
label={t("Resource.Description")}
|
||||
variant="outlined"
|
||||
|
|
|
@ -6,7 +6,6 @@ import { LoadingText } from "../../../../components";
|
|||
import ResourceComponent from "./ResourceComponent";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import style from "../styles";
|
||||
import { useToast } from "../../../../hooks";
|
||||
|
||||
const useStyles = makeStyles(style);
|
||||
|
||||
|
@ -21,7 +20,6 @@ const ResourceContainer = () => {
|
|||
const params = useParams();
|
||||
const { getResource } = useResourcesApi();
|
||||
const { getMimeTypes, getResourceCategories } = useDictionariesApi();
|
||||
const { success } = useToast();
|
||||
|
||||
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";
|
||||
|
||||
const useResourceSecurity = () => {
|
||||
const { token } = useUserState();
|
||||
const { token } = useTuitioToken();
|
||||
const secureUrl = useCallback(
|
||||
(url) => {
|
||||
const separator = url.includes("?") ? "&" : "?";
|
||||
const securedUrl = `${url}${separator}token=${token.raw}`;
|
||||
const securedUrl = `${url}${separator}token=${token}`;
|
||||
return securedUrl;
|
||||
},
|
||||
[token.raw]
|
||||
[token]
|
||||
);
|
||||
|
||||
return { secureUrl };
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
import React, { Suspense } from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { TuitioProvider } from "@flare/tuitio-client-react";
|
||||
import { ThemeProvider } from "@material-ui/styles";
|
||||
import { CssBaseline } from "@material-ui/core";
|
||||
import Themes from "./themes";
|
||||
import App from "./components/App";
|
||||
import * as serviceWorker from "./serviceWorker";
|
||||
import { LayoutProvider } from "./contexts/LayoutContext";
|
||||
import { UserProvider } from "./contexts/UserContext";
|
||||
import { ToastProvider } from "./contexts/ToastContext";
|
||||
import "./utils/i18n";
|
||||
|
||||
ReactDOM.render(
|
||||
<LayoutProvider>
|
||||
<UserProvider>
|
||||
<TuitioProvider tuitioUrl={process.env.REACT_APP_TUITIO_URL}>
|
||||
<ThemeProvider theme={Themes.default}>
|
||||
<CssBaseline />
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
|
@ -21,7 +21,7 @@ ReactDOM.render(
|
|||
</ToastProvider>
|
||||
</Suspense>
|
||||
</ThemeProvider>
|
||||
</UserProvider>
|
||||
</TuitioProvider>
|
||||
</LayoutProvider>,
|
||||
document.getElementById("root")
|
||||
);
|
||||
|
|
|
@ -18,23 +18,32 @@ import useStyles from "./styles";
|
|||
// logo
|
||||
import logo from "./logo.svg";
|
||||
import google from "../../images/google.svg";
|
||||
import { useToast } from "../../hooks";
|
||||
import { useTuitioClient } from "@flare/tuitio-client-react";
|
||||
|
||||
// context
|
||||
import { useUserDispatch, loginUser } from "../../contexts/UserContext";
|
||||
function Login() {
|
||||
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) {
|
||||
var classes = useStyles();
|
||||
const { error } = useToast();
|
||||
const { login } = useTuitioClient({
|
||||
onLoginSuccess: () => setIsLoading(false),
|
||||
onLoginFailed: () => error(t("Login.IncorrectCredentials")),
|
||||
onLoginError: (err) => {
|
||||
setErrorEncountered(true);
|
||||
error(err.message);
|
||||
}
|
||||
});
|
||||
|
||||
// global
|
||||
var userDispatch = useUserDispatch();
|
||||
|
||||
// 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("");
|
||||
const handleLogin = () => {
|
||||
setIsLoading(true);
|
||||
login(loginValue, passwordValue);
|
||||
};
|
||||
|
||||
return (
|
||||
<Grid container className={classes.container}>
|
||||
|
@ -68,7 +77,7 @@ function Login(props) {
|
|||
<Typography className={classes.formDividerWord}>or</Typography>
|
||||
<div className={classes.formDivider} />
|
||||
</div>
|
||||
<Fade in={error}>
|
||||
<Fade in={errorEncountered}>
|
||||
<Typography color="secondary" className={classes.errorMessage}>
|
||||
Something is wrong with your login or password :(
|
||||
</Typography>
|
||||
|
@ -111,16 +120,7 @@ function Login(props) {
|
|||
disabled={
|
||||
loginValue.length === 0 || passwordValue.length === 0
|
||||
}
|
||||
onClick={() =>
|
||||
loginUser(
|
||||
userDispatch,
|
||||
loginValue,
|
||||
passwordValue,
|
||||
props.history,
|
||||
setIsLoading,
|
||||
setError
|
||||
)
|
||||
}
|
||||
onClick={handleLogin}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
size="large"
|
||||
|
@ -201,16 +201,7 @@ function Login(props) {
|
|||
<CircularProgress size={26} />
|
||||
) : (
|
||||
<Button
|
||||
onClick={() =>
|
||||
loginUser(
|
||||
userDispatch,
|
||||
loginValue,
|
||||
passwordValue,
|
||||
props.history,
|
||||
setIsLoading,
|
||||
setError
|
||||
)
|
||||
}
|
||||
onClick={handleLogin}
|
||||
disabled={
|
||||
loginValue.length === 0 ||
|
||||
passwordValue.length === 0 ||
|
||||
|
|
|
@ -1,32 +1,31 @@
|
|||
import defaultTheme from "./default";
|
||||
|
||||
import { createMuiTheme } from "@material-ui/core";
|
||||
import { createTheme } from "@material-ui/core/styles";
|
||||
|
||||
const overrides = {
|
||||
typography: {
|
||||
h1: {
|
||||
fontSize: "3rem",
|
||||
fontSize: "3rem"
|
||||
},
|
||||
h2: {
|
||||
fontSize: "2rem",
|
||||
fontSize: "2rem"
|
||||
},
|
||||
h3: {
|
||||
fontSize: "1.64rem",
|
||||
fontSize: "1.64rem"
|
||||
},
|
||||
h4: {
|
||||
fontSize: "1.5rem",
|
||||
fontSize: "1.5rem"
|
||||
},
|
||||
h5: {
|
||||
fontSize: "1.285rem",
|
||||
fontSize: "1.285rem"
|
||||
},
|
||||
h6: {
|
||||
fontSize: "1.142rem",
|
||||
},
|
||||
},
|
||||
fontSize: "1.142rem"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const themes = {
|
||||
default: createMuiTheme({ ...defaultTheme, ...overrides }),
|
||||
default: createTheme({ ...defaultTheme, ...overrides })
|
||||
};
|
||||
|
||||
export default themes;
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
import axios from "axios";
|
||||
import i18next from "i18next";
|
||||
import { getItem } from "./localStorage";
|
||||
import { storageKeys } from "./identity";
|
||||
import { fetch as fetchTuitioData } from "@flare/tuitio-client";
|
||||
|
||||
function getHeaders() {
|
||||
const token = getItem(storageKeys.TOKEN);
|
||||
const { token } = fetchTuitioData();
|
||||
const language = i18next.language;
|
||||
|
||||
return {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Basic ${token.raw}`,
|
||||
Authorization: `Tuitio ${token}`,
|
||||
"Accept-Language": `${language}`
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
import { request } from "./axios";
|
||||
import { setItem, getItem, removeItem } from "./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 };
|
|
@ -1,38 +0,0 @@
|
|||
import { typeValidator } from "@flare/js-utils";
|
||||
|
||||
const setItem = (key, value) => {
|
||||
const { isArray, isObject } = typeValidator;
|
||||
let valueToStore = value;
|
||||
if (isArray(value) || isObject(value)) {
|
||||
valueToStore = JSON.stringify(value);
|
||||
}
|
||||
|
||||
window.localStorage.setItem(key, valueToStore);
|
||||
};
|
||||
|
||||
const getItem = (key) => {
|
||||
var value = window.localStorage.getItem(key);
|
||||
const { isJson } = typeValidator;
|
||||
var { data, success } = isJson(value);
|
||||
|
||||
if (success) {
|
||||
return data;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
const removeItem = (key) => {
|
||||
window.localStorage.removeItem(key);
|
||||
};
|
||||
|
||||
const clear = () => {
|
||||
window.localStorage.clear();
|
||||
};
|
||||
|
||||
const key = (index) => {
|
||||
var keyName = window.localStorage.key(index);
|
||||
return keyName;
|
||||
};
|
||||
|
||||
export { setItem, getItem, removeItem, clear, key };
|
Loading…
Reference in New Issue