зеркало из https://github.com/mozilla/hubs.git
Added SpinWhileTrue, fixed styling, added modals, removed routing for /create endpoint in favor of single page for simplicity
This commit is contained in:
Родитель
4ac95414d0
Коммит
52734b47ec
2
.babelrc
2
.babelrc
|
@ -39,4 +39,4 @@
|
|||
"@babel/plugin-transform-async-to-generator",
|
||||
"@babel/plugin-proposal-optional-chaining"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Spinner } from "../misc/Spinner";
|
||||
import { Center } from "./Center";
|
||||
|
||||
export function SpinWhileTrue({ isSpinning, children }) {
|
||||
return (
|
||||
<>
|
||||
{isSpinning ? (
|
||||
<Center>
|
||||
<Spinner />
|
||||
</Center>
|
||||
) : (
|
||||
<>{children}</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
SpinWhileTrue.propTypes = {
|
||||
isSpinning: PropTypes.bool.isRequired
|
||||
};
|
|
@ -16,6 +16,10 @@
|
|||
margin: 0px !important;
|
||||
}
|
||||
|
||||
:local(.textError) {
|
||||
color: theme.$red
|
||||
}
|
||||
|
||||
$spacing: (
|
||||
"2xs": theme.$spacing-2xs,
|
||||
"xs": theme.$spacing-xs,
|
||||
|
|
|
@ -12,6 +12,7 @@ import { CheckboxInput } from "../input/CheckboxInput";
|
|||
import { RadioInputField } from "../input/RadioInputField";
|
||||
import { RadioInputOption } from "../input/RadioInput";
|
||||
import { useIntl, defineMessages } from "react-intl";
|
||||
import { SpinWhileTrue } from "../layout/SpinWhileTrue";
|
||||
|
||||
const tokenTypes = ["account", "app"];
|
||||
|
||||
|
@ -23,78 +24,82 @@ export const CreateToken = ({
|
|||
toggleTokenType,
|
||||
error,
|
||||
showNoScopesSelectedError,
|
||||
onCreateToken
|
||||
onCreateToken,
|
||||
onClose,
|
||||
isPending
|
||||
}) => (
|
||||
<div>
|
||||
<h1>
|
||||
<FormattedMessage id="new-token.title" defaultMessage="New Token" />
|
||||
</h1>
|
||||
{error && (
|
||||
<Row className>
|
||||
<p>{`An Error occured: ${error}`}</p>
|
||||
<SpinWhileTrue isSpinning={isPending}>
|
||||
{error && (
|
||||
<Row padding="sm" className={styles.revokeWarning}>
|
||||
<p>{`An Error occured: ${error}`}</p>
|
||||
</Row>
|
||||
)}
|
||||
<Row gap="xl" breakpointColumn="md" className={styleUtils.smMarginY}>
|
||||
<h2 className={styleUtils.flexBasis40}>
|
||||
<FormattedMessage id="new-token.token-type" defaultMessage="Token type" />
|
||||
</h2>
|
||||
<Row className={styleUtils.flexBasis60}>
|
||||
<RadioInputField className={styles.flexDirectionRow} inputClassName={styles.flexDirectionRow}>
|
||||
{tokenTypes.map(tokenType => (
|
||||
<RadioInputOption
|
||||
key={tokenType}
|
||||
className={classNames(styleUtils.flexBasis50, styleUtils.margin0)}
|
||||
labelClassName={styles.radioLabel}
|
||||
checked={tokenType === selectedTokenType}
|
||||
onChange={() => toggleTokenType(tokenType)}
|
||||
value={tokenType}
|
||||
label={tokenType.charAt(0).toUpperCase() + tokenType.slice(1)}
|
||||
/>
|
||||
))}
|
||||
</RadioInputField>
|
||||
</Row>
|
||||
</Row>
|
||||
)}
|
||||
<Row gap="xl" breakpointColumn="md" topMargin="sm">
|
||||
<h2 className={styleUtils.flexBasis40}>
|
||||
<FormattedMessage id="new-token.token-type" defaultMessage="Token type" />
|
||||
</h2>
|
||||
<Row className={styleUtils.flexBasis60}>
|
||||
<RadioInputField className={styles.flexDirectionRow} inputClassName={styles.flexDirectionRow}>
|
||||
{tokenTypes.map(tokenType => (
|
||||
<RadioInputOption
|
||||
key={tokenType}
|
||||
className={classNames(styleUtils.flexBasis50, styleUtils.margin0)}
|
||||
labelClassName={styles.radioLabel}
|
||||
checked={tokenType === selectedTokenType}
|
||||
onChange={() => toggleTokenType(tokenType)}
|
||||
value={tokenType}
|
||||
label={tokenType.charAt(0).toUpperCase() + tokenType.slice(1)}
|
||||
/>
|
||||
))}
|
||||
</RadioInputField>
|
||||
</Row>
|
||||
</Row>
|
||||
<Divider />
|
||||
<Column gap="xl">
|
||||
<h2>
|
||||
<FormattedMessage id="new-token.select-scopes-title" defaultMessage="Select scopes" />
|
||||
</h2>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="new-token.select-scopes-description"
|
||||
defaultMessage="Set the level of access this token will have by choosing from the scopes list."
|
||||
/>
|
||||
</p>
|
||||
</Column>
|
||||
<Column>
|
||||
{scopes.map(scopeName => {
|
||||
return (
|
||||
<SelectScope
|
||||
key={scopeName}
|
||||
scopeName={scopeName}
|
||||
toggleSelectedScopes={toggleSelectedScopes}
|
||||
selected={selectedScopes.includes(scopeName)}
|
||||
<Divider />
|
||||
<Column gap="xl" className={styleUtils.mdMarginY}>
|
||||
<h2>
|
||||
<FormattedMessage id="new-token.select-scopes-title" defaultMessage="Select scopes" />
|
||||
</h2>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="new-token.select-scopes-description"
|
||||
defaultMessage="Set the level of access this token will have by choosing from the scopes list."
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Column>
|
||||
{showNoScopesSelectedError && (
|
||||
<Row>
|
||||
<p>Please select at least one scope.</p>
|
||||
</p>
|
||||
</Column>
|
||||
<Column className={styleUtils.mdMarginY}>
|
||||
{scopes.map(scopeName => {
|
||||
return (
|
||||
<SelectScope
|
||||
key={scopeName}
|
||||
scopeName={scopeName}
|
||||
toggleSelectedScopes={toggleSelectedScopes}
|
||||
selected={selectedScopes.includes(scopeName)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Column>
|
||||
{showNoScopesSelectedError && (
|
||||
<Row className={styleUtils.mdMarginY}>
|
||||
<p className={styleUtils.textError}>Please select at least one scope.</p>
|
||||
</Row>
|
||||
)}
|
||||
<Row spaceBetween className={styleUtils.xlMarginBottom}>
|
||||
<Button sm preset="basic" onClick={() => onClose({ createdNewToken: false })}>
|
||||
<FormattedMessage id="new-token.back" defaultMessage="Back" />
|
||||
</Button>
|
||||
<Button
|
||||
sm
|
||||
preset="primary"
|
||||
onClick={() => onCreateToken({ tokenType: selectedTokenType, scopes: selectedScopes })}
|
||||
>
|
||||
<FormattedMessage id="new-token.generate" defaultMessage="Generate" />
|
||||
</Button>
|
||||
</Row>
|
||||
)}
|
||||
<Row spaceBetween className={styleUtils.xlMarginBottom}>
|
||||
<Button sm preset="basic">
|
||||
<FormattedMessage id="new-token.back" defaultMessage="Back" />
|
||||
</Button>
|
||||
<Button
|
||||
sm
|
||||
preset="primary"
|
||||
onClick={() => onCreateToken({ tokenType: selectedTokenType, scopes: selectedScopes })}
|
||||
>
|
||||
<FormattedMessage id="new-token.generate" defaultMessage="Generate" />
|
||||
</Button>
|
||||
</Row>
|
||||
</SpinWhileTrue>
|
||||
</div>
|
||||
);
|
||||
|
||||
|
@ -106,7 +111,9 @@ CreateToken.propTypes = {
|
|||
showNoScopesSelectedError: PropTypes.bool,
|
||||
toggleTokenType: PropTypes.func,
|
||||
selectedTokenType: PropTypes.string,
|
||||
onCreateToken: PropTypes.func
|
||||
onCreateToken: PropTypes.func,
|
||||
onClose: PropTypes.func,
|
||||
isPending: PropTypes.bool
|
||||
};
|
||||
|
||||
// Scope info that is localized by language
|
||||
|
@ -125,7 +132,7 @@ const scopeInfo = {
|
|||
defaultMessage: "Read room data"
|
||||
}
|
||||
}),
|
||||
// for storybook example
|
||||
// For storybook long scope example in Tokens.stories.js
|
||||
another_long_scope_here: defineMessages({
|
||||
description: {
|
||||
id: "new-token-scopes.write-rooms.description",
|
||||
|
@ -141,7 +148,10 @@ const SelectScope = ({ scopeName, selected, toggleSelectedScopes }) => {
|
|||
<Row
|
||||
padding="sm"
|
||||
breakpointColumn="md"
|
||||
className={classNames(styles.backgroundWhite, { [styles.selectedBorder]: selected })}
|
||||
className={classNames(styles.backgroundWhite, {
|
||||
[styles.unselectedBorder]: !selected,
|
||||
[styles.selectedBorder]: selected
|
||||
})}
|
||||
topMargin="md"
|
||||
>
|
||||
<Row className={classNames(styleUtils.flexBasis40, styles.wordWrap)}>
|
||||
|
|
|
@ -4,6 +4,7 @@ import PropTypes from "prop-types";
|
|||
import { CreateToken } from "./CreateToken";
|
||||
import { RevealTokenModal } from "./RevealTokenModal";
|
||||
import { createToken, fetchAvailableScopes } from "./token-utils";
|
||||
import { CenteredModalWrapper } from "../layout/CenteredModalWrapper";
|
||||
|
||||
const CreateTokenActions = {
|
||||
submitCreateToken: "submitCreateToken",
|
||||
|
@ -17,22 +18,15 @@ const CreateTokenActions = {
|
|||
toggleTokenTypeChange: "toggleTokenTypeChange"
|
||||
};
|
||||
|
||||
const steps = {
|
||||
selectScopes: "selectScopes",
|
||||
success: "success",
|
||||
pending: "pending",
|
||||
error: "error"
|
||||
};
|
||||
|
||||
const initialCreateTokenState = {
|
||||
step: steps.selectScopes,
|
||||
scopes: [],
|
||||
selectedScopes: [],
|
||||
selectedTokenType: "account",
|
||||
token: "",
|
||||
error: "",
|
||||
showNoScopesSelectedError: false,
|
||||
showRevealTokenModal: false
|
||||
showRevealTokenModal: false,
|
||||
isPending: false
|
||||
};
|
||||
|
||||
function createTokenReducer(state, action) {
|
||||
|
@ -41,16 +35,15 @@ function createTokenReducer(state, action) {
|
|||
console.log(action);
|
||||
switch (action.type) {
|
||||
case CreateTokenActions.submitCreateToken:
|
||||
return { ...state, step: steps.pending };
|
||||
return { ...state, isPending: true };
|
||||
case CreateTokenActions.createTokenSuccess:
|
||||
return { ...state, showRevealTokenModal: true, token: action.token };
|
||||
return { ...state, isPending: false, showRevealTokenModal: true, token: action.token };
|
||||
case CreateTokenActions.createTokenError:
|
||||
return { ...state, error: action.errorMsg };
|
||||
return { ...state, isPending: false, error: action.errorMsg };
|
||||
case CreateTokenActions.fetchingScopesSuccess:
|
||||
console.log("FETCHED SCOPES");
|
||||
return { ...state, scopes: action.scopes };
|
||||
case CreateTokenActions.fetchingScopesError:
|
||||
return { ...state, step: steps.error, error: "Error fetching scopes, please try again later." };
|
||||
return { ...state, error: "Error fetching scopes, please try again later." };
|
||||
case CreateTokenActions.showNoScopesError:
|
||||
return { ...state, showNoScopesSelectedError: true };
|
||||
case CreateTokenActions.toggleScopeChange: {
|
||||
|
@ -76,7 +69,6 @@ function useCreateToken() {
|
|||
const [state, dispatch] = useReducer(createTokenReducer, initialCreateTokenState);
|
||||
|
||||
const onCreateToken = async ({ tokenType, scopes }) => {
|
||||
// TODO add no scopes error to the view
|
||||
if (scopes.length === 0) return dispatch({ type: CreateTokenActions.showNoScopesError });
|
||||
|
||||
dispatch({ type: CreateTokenActions.submitCreateToken });
|
||||
|
@ -112,7 +104,7 @@ function useCreateToken() {
|
|||
};
|
||||
|
||||
return {
|
||||
step: state.step,
|
||||
isPending: state.isPending,
|
||||
scopes: state.scopes,
|
||||
selectedScopes: state.selectedScopes,
|
||||
token: state.token,
|
||||
|
@ -139,7 +131,8 @@ export const CreateTokenContainer = ({ onClose }) => {
|
|||
fetchScopes,
|
||||
toggleSelectedScopes,
|
||||
toggleTokenType,
|
||||
selectedTokenType
|
||||
selectedTokenType,
|
||||
isPending
|
||||
} = useCreateToken();
|
||||
|
||||
useEffect(
|
||||
|
@ -151,7 +144,15 @@ export const CreateTokenContainer = ({ onClose }) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
{showRevealTokenModal && <RevealTokenModal token={token} selectedScopes={selectedScopes} onClose={onClose} />}
|
||||
{showRevealTokenModal && (
|
||||
<CenteredModalWrapper>
|
||||
<RevealTokenModal
|
||||
token={token}
|
||||
selectedScopes={selectedScopes}
|
||||
onClose={() => onClose({ createdNewToken: true })}
|
||||
/>
|
||||
</CenteredModalWrapper>
|
||||
)}
|
||||
<CreateToken
|
||||
showNoScopesSelectedError={showNoScopesSelectedError}
|
||||
onCreateToken={onCreateToken}
|
||||
|
@ -161,7 +162,13 @@ export const CreateTokenContainer = ({ onClose }) => {
|
|||
toggleSelectedScopes={toggleSelectedScopes}
|
||||
toggleTokenType={toggleTokenType}
|
||||
error={error}
|
||||
onClose={onClose}
|
||||
isPending={isPending}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
CreateTokenContainer.propTypes = {
|
||||
onClose: PropTypes.func
|
||||
};
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from "react";
|
|||
import styles from "./Tokens.scss";
|
||||
import styleUtils from "../styles/style-utils.scss";
|
||||
import { Row } from "../layout/Row";
|
||||
// import { ReactComponent as HubsDuckIcon } from "../../assets/images/footer-duck.svg";
|
||||
import { ReactComponent as HubsDuckIcon } from "../../assets/images/footer-duck.svg";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Column } from "../layout/Column";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
|
|
@ -14,7 +14,6 @@ import classNames from "classnames";
|
|||
import { CopyableTextInputField } from "../input/CopyableTextInputField";
|
||||
|
||||
export const RevealTokenModal = ({ token, selectedScopes, onClose }) => {
|
||||
// TODO add copy token functionality
|
||||
return (
|
||||
<Modal
|
||||
titleNode={
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Spinner } from "../misc/Spinner";
|
||||
import { CenteredModalWrapper } from "../layout/CenteredModalWrapper";
|
||||
import { RevokeTokenModal } from "./RevokeTokenModal";
|
||||
import { revokeToken } from "./token-utils";
|
||||
|
||||
export function RevokeTokenContainer({ onClose, tokenId }) {
|
||||
const [error, setError] = useState("");
|
||||
const [isPending, setIsPending] = useState(false);
|
||||
|
||||
const onConfirmRevoke = async () => {
|
||||
setIsPending(true);
|
||||
try {
|
||||
await revokeToken({ id: tokenId });
|
||||
onClose({ removedTokenId: tokenId });
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
}
|
||||
setIsPending(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<CenteredModalWrapper>
|
||||
<RevokeTokenModal
|
||||
isPending={isPending}
|
||||
error={error}
|
||||
onRevoke={onConfirmRevoke}
|
||||
onClose={() => onClose({ removedTokenId: null })}
|
||||
/>
|
||||
</CenteredModalWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
RevokeTokenContainer.propTypes = {
|
||||
onClose: PropTypes.func,
|
||||
tokenId: PropTypes.string
|
||||
};
|
|
@ -10,57 +10,71 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|||
import { faExclamationTriangle } from "@fortawesome/free-solid-svg-icons/faExclamationTriangle";
|
||||
import { faTimes } from "@fortawesome/free-solid-svg-icons/faTimes";
|
||||
import { Column } from "../layout/Column";
|
||||
import { SpinWhileTrue } from "../layout/SpinWhileTrue";
|
||||
|
||||
export function RevokeTokenModal({ onClose, onRevoke }) {
|
||||
export function RevokeTokenModal({ onClose, onRevoke, error, isPending }) {
|
||||
return (
|
||||
<Modal
|
||||
title={<FormattedMessage id="revoke-token-modal.title" defaultMessage="Revoke token" />}
|
||||
beforeTitle={<FontAwesomeIcon icon={faExclamationTriangle} />}
|
||||
afterTitle={<FontAwesomeIcon icon={faTimes} onClick={onClose} />}
|
||||
afterTitle={
|
||||
<div className={styles.closeModalButton}>
|
||||
<FontAwesomeIcon onClick={onClose} icon={faTimes} />
|
||||
</div>
|
||||
}
|
||||
disableFullscreen
|
||||
className={styles.maxWidth400}
|
||||
>
|
||||
<Column padding="sm">
|
||||
<Column className={styles.revokeDescription}>
|
||||
<p className={styleUtils.xsMarginBottom}>
|
||||
<FormattedMessage
|
||||
id="revoke-token-modal.description1"
|
||||
defaultMessage="Are you sure you want to revoke this token?"
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="revoke-token-modal.description2"
|
||||
defaultMessage="Any scripts or requests relying on this token will lose access."
|
||||
/>
|
||||
</p>
|
||||
<SpinWhileTrue isSpinning={isPending}>
|
||||
{error && (
|
||||
<Row padding="sm" className={styles.revokeWarning}>
|
||||
<p>{`An Error occured: ${error}`}</p>
|
||||
</Row>
|
||||
)}
|
||||
<Column padding="sm">
|
||||
<Column className={styles.revokeDescription}>
|
||||
<p className={styleUtils.xsMarginBottom}>
|
||||
<FormattedMessage
|
||||
id="revoke-token-modal.description1"
|
||||
defaultMessage="Are you sure you want to revoke this token?"
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="revoke-token-modal.description2"
|
||||
defaultMessage="Any scripts or requests relying on this token will lose access."
|
||||
/>
|
||||
</p>
|
||||
</Column>
|
||||
<Row padding="sm" className={styles.revokeWarning}>
|
||||
<p>
|
||||
<FormattedMessage id="revoke-token-modal.revoke-warning-1" defaultMessage="This action is" />{" "}
|
||||
<b>
|
||||
<FormattedMessage id="revoke-token-modal.revoke-warning-2" defaultMessage="permanent" />
|
||||
</b>{" "}
|
||||
<FormattedMessage id="revoke-token-modal.revoke-warning-3" defaultMessage="and" />{" "}
|
||||
<b>
|
||||
<FormattedMessage id="revoke-token-modal.revoke-warning-4" defaultMessage="can not be undone." />
|
||||
</b>
|
||||
</p>
|
||||
</Row>
|
||||
<Row spaceBetween padding="sm">
|
||||
<Button preset="basic" sm onClick={onClose}>
|
||||
<FormattedMessage id="revoke-token-modal.cancel" defaultMessage="Cancel" />
|
||||
</Button>
|
||||
<Button preset="accent1" sm onClick={onRevoke}>
|
||||
<FormattedMessage id="revoke-token-modal.revoke" defaultMessage="Revoke" />
|
||||
</Button>
|
||||
</Row>
|
||||
</Column>
|
||||
<Row padding="sm" className={styles.revokeWarning}>
|
||||
<p>
|
||||
<FormattedMessage id="revoke-token-modal.revoke-warning-1" defaultMessage="This action is" />{" "}
|
||||
<b>
|
||||
<FormattedMessage id="revoke-token-modal.revoke-warning-2" defaultMessage="permanent" />
|
||||
</b>{" "}
|
||||
<FormattedMessage id="revoke-token-modal.revoke-warning-3" defaultMessage="and" />{" "}
|
||||
<b>
|
||||
<FormattedMessage id="revoke-token-modal.revoke-warning-4" defaultMessage="can not be undone." />
|
||||
</b>
|
||||
</p>
|
||||
</Row>
|
||||
<Row spaceBetween padding="sm">
|
||||
<Button preset="basic" sm onClick={onClose}>
|
||||
<FormattedMessage id="revoke-token-modal.cancel" defaultMessage="Cancel" />
|
||||
</Button>
|
||||
<Button preset="accent1" sm onClick={onRevoke}>
|
||||
<FormattedMessage id="revoke-token-modal.revoke" defaultMessage="Revoke" />
|
||||
</Button>
|
||||
</Row>
|
||||
</Column>
|
||||
</SpinWhileTrue>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
RevokeTokenModal.propTypes = {
|
||||
onClose: PropTypes.func.isRequired,
|
||||
onRevoke: PropTypes.func.isRequired
|
||||
onRevoke: PropTypes.func.isRequired,
|
||||
error: PropTypes.string,
|
||||
isPending: PropTypes.bool
|
||||
};
|
||||
|
|
|
@ -3,14 +3,14 @@ import PropTypes from "prop-types";
|
|||
import { Token } from "./Token";
|
||||
import { Row } from "../layout/Row";
|
||||
import { Column } from "../layout/Column";
|
||||
import { Center } from "../layout/Center";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { Button } from "../input/Button";
|
||||
import styleUtils from "../styles/style-utils.scss";
|
||||
import styles from "./Tokens.scss";
|
||||
import { Link } from "react-router-dom";
|
||||
import { SpinWhileTrue } from "../layout/SpinWhileTrue";
|
||||
import { Center } from "../layout/Center";
|
||||
|
||||
export const TokenList = ({ tokens, onRevokeToken, onCreateToken }) => {
|
||||
export const TokenList = ({ tokens, onRevokeToken, showCreateToken, error, isFetching }) => {
|
||||
return (
|
||||
<div>
|
||||
<TokenMenuHeader />
|
||||
|
@ -18,35 +18,44 @@ export const TokenList = ({ tokens, onRevokeToken, onCreateToken }) => {
|
|||
<h2>
|
||||
<FormattedMessage id="empty-token.title2" defaultMessage="Token List" />
|
||||
</h2>
|
||||
<Link to="/tokens/create">
|
||||
<Button preset="primary" sm>
|
||||
<FormattedMessage id="tokens.button-create-token" defaultMessage="Create token" />
|
||||
</Button>
|
||||
</Link>
|
||||
<Button preset="primary" sm onClick={showCreateToken}>
|
||||
<FormattedMessage id="tokens.button-create-token" defaultMessage="Create token" />
|
||||
</Button>
|
||||
</Row>
|
||||
{tokens && tokens.length ? (
|
||||
<Column className={styleUtils.xlMarginY}>
|
||||
{tokens.map(token => <Token key={token.id} tokenInfo={token} onRevokeToken={onRevokeToken} />)}
|
||||
</Column>
|
||||
) : (
|
||||
<div className={styleUtils.xlMarginY}>
|
||||
<Row padding="md" className={styles.backgroundWhite}>
|
||||
<Center>
|
||||
<FormattedMessage id="tokens.no-tokens-created" defaultMessage="No tokens created." />
|
||||
</Center>
|
||||
<SpinWhileTrue isSpinning={isFetching}>
|
||||
{error && (
|
||||
<Row padding="sm" className={styles.revokeWarning}>
|
||||
<p>{`An Error occured: ${error}`}</p>
|
||||
</Row>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
{tokens && tokens.length ? (
|
||||
<Column className={styleUtils.xlMarginY}>
|
||||
{tokens.map(token => (
|
||||
<Token key={token.id} tokenInfo={token} onRevokeToken={() => onRevokeToken({ revokeId: token.id })} />
|
||||
))}
|
||||
</Column>
|
||||
) : (
|
||||
<div className={styleUtils.xlMarginY}>
|
||||
<Row padding="md" className={styles.backgroundWhite}>
|
||||
<Center>
|
||||
<FormattedMessage id="tokens.no-tokens-created" defaultMessage="No tokens created." />
|
||||
</Center>
|
||||
</Row>
|
||||
</div>
|
||||
)}
|
||||
</SpinWhileTrue>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
TokenList.propTypes = {
|
||||
tokens: PropTypes.array,
|
||||
onRevokeToken: PropTypes.func
|
||||
onRevokeToken: PropTypes.func,
|
||||
showCreateToken: PropTypes.func,
|
||||
error: PropTypes.string,
|
||||
isFetching: PropTypes.bool
|
||||
};
|
||||
|
||||
// TODO move to TokenContainer when defining token state
|
||||
const TokenMenuHeader = () => (
|
||||
<Column gap="xl">
|
||||
<h1>
|
||||
|
|
|
@ -60,12 +60,16 @@ $tag-top-margin: theme.$spacing-xs;
|
|||
border: 2px solid theme.$blue;
|
||||
}
|
||||
|
||||
:local(.unselected-border) {
|
||||
border: 2px solid theme.$background1-color;
|
||||
}
|
||||
|
||||
:local(.word-wrap) {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
:local(.flex-direction-row) {
|
||||
flex-direction: row;
|
||||
flex-direction: row !important;
|
||||
}
|
||||
|
||||
:local(.padding-Y-xl) {
|
||||
|
@ -82,7 +86,7 @@ $tag-top-margin: theme.$spacing-xs;
|
|||
}
|
||||
|
||||
:local(.max-width-auto) {
|
||||
max-width: none;
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
:local(.max-width-400) {
|
||||
|
|
|
@ -1,77 +1,76 @@
|
|||
import React, { useState, useEffect, useContext } from "react";
|
||||
import { Token } from "./Token";
|
||||
import { fetchMyTokens } from "./token-utils";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import styles from "./Token.scss";
|
||||
import { RevokeTokenModal } from "./RevokeTokenModal";
|
||||
import { RevealTokenModal } from "./RevealTokenModal";
|
||||
import { RevokeTokenContainer } from "./RevokeTokenContainer";
|
||||
import { TokenList } from "./TokenList";
|
||||
import { NoAccess } from "./NoAccess";
|
||||
import { CenteredModalWrapper } from "../layout/CenteredModalWrapper";
|
||||
import { AuthContext } from "../auth/AuthContext";
|
||||
import { BrowserRouter, Switch, Route } from "react-router-dom";
|
||||
import { CreateTokenContainer } from "./CreateTokenContainer";
|
||||
import { CreateToken } from "./CreateToken";
|
||||
|
||||
export function TokensContainer() {
|
||||
const [tokens, setTokens] = useState([]);
|
||||
const [showRevealTokenModal, setRevealTokenModal] = useState(false);
|
||||
// const [showRevokeTokenModal, setShowRevokeTokenModal] = useState(false);
|
||||
// const [selectedRevokeId, setSelectedRevokeId] = useState();
|
||||
const [isFetching, setIsFetching] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
const [showCreateToken, setShowCreateToken] = useState(false);
|
||||
const [showRevokeTokenModal, setShowRevokeTokenModal] = useState(false);
|
||||
const [selectedRevokeId, setSelectedRevokeId] = useState();
|
||||
|
||||
const auth = useContext(AuthContext); // Re-render when you log in/out.
|
||||
console.log(auth);
|
||||
console.log(NoAccess);
|
||||
|
||||
const fetchTokens = async () => {
|
||||
try {
|
||||
setIsFetching(true);
|
||||
setTokens(await fetchMyTokens());
|
||||
setIsFetching(false);
|
||||
} catch (err) {
|
||||
setError("Error fetching tokens: " + err.message);
|
||||
setIsFetching(false);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
async function updateTokens() {
|
||||
setTokens(await fetchMyTokens());
|
||||
await fetchTokens();
|
||||
}
|
||||
if (auth?.isAdmin) updateTokens();
|
||||
},
|
||||
[auth.isAdmin]
|
||||
);
|
||||
|
||||
const onRevealTokenModalClose = async ({ createdNewToken }) => {
|
||||
// setRevealTokenModal(false);
|
||||
// if (createdNewToken) {
|
||||
// setTokens(await fetchMyTokens());
|
||||
// }
|
||||
const onCreateTokenClose = async ({ createdNewToken }) => {
|
||||
setShowCreateToken(false);
|
||||
if (createdNewToken) await fetchTokens();
|
||||
};
|
||||
|
||||
const onRevokeToken = ({ revokeId }) => {
|
||||
setShowRevokeTokenModal(true);
|
||||
setSelectedRevokeId(revokeId);
|
||||
};
|
||||
|
||||
const onRevokeTokenClose = ({ removedTokenId }) => {
|
||||
// if (removedTokenId) setTokens(tokens.filter(token => token.id !== removedTokenId));
|
||||
// setShowRevokeTokenModal(false);
|
||||
// setSelectedRevokeId("");
|
||||
if (removedTokenId) setTokens(tokens.filter(token => token.id !== removedTokenId));
|
||||
setShowRevokeTokenModal(false);
|
||||
setSelectedRevokeId("");
|
||||
};
|
||||
|
||||
console.log(auth);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
// {showRevealTokenModal && (
|
||||
// <CenteredModalWrapper>
|
||||
// <RevealTokenModal onClose={onRevealTokenModalClose} />
|
||||
// </CenteredModalWrapper>
|
||||
// )}
|
||||
// {showRevokeTokenModal && (
|
||||
// <CenteredModalWrapper>
|
||||
// <RevokeTokenModal selectedId={selectedRevokeId} onClose={onRevokeTokenClose} />
|
||||
// </CenteredModalWrapper>
|
||||
// )}
|
||||
}
|
||||
{showRevokeTokenModal && <RevokeTokenContainer tokenId={selectedRevokeId} onClose={onRevokeTokenClose} />}
|
||||
{auth?.isAdmin ? (
|
||||
<BrowserRouter>
|
||||
<Switch>
|
||||
<Route path="/tokens/create">
|
||||
<CreateTokenContainer />
|
||||
</Route>
|
||||
<Route path="/">
|
||||
<TokenList tokens={tokens} onRevokeToken={onRevokeTokenClose} />
|
||||
</Route>
|
||||
</Switch>
|
||||
</BrowserRouter>
|
||||
showCreateToken ? (
|
||||
<CreateTokenContainer onClose={onCreateTokenClose} />
|
||||
) : (
|
||||
<TokenList
|
||||
error={error}
|
||||
isFetching={isFetching}
|
||||
tokens={tokens}
|
||||
onRevokeToken={onRevokeToken}
|
||||
showCreateToken={() => {
|
||||
setShowCreateToken(true);
|
||||
}}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
<NoAccess />
|
||||
)}
|
||||
|
|
Загрузка…
Ссылка в новой задаче