mirror of
https://github.com/elyby/accounts-frontend.git
synced 2024-12-02 11:41:04 +05:30
Split ApplicationsIndex into smaller parts
This commit is contained in:
parent
4931ea5598
commit
acbf61ab40
@ -6,7 +6,6 @@ import { FormattedMessage as Message } from 'react-intl';
|
|||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
import { LinkButton } from 'components/ui/form';
|
import { LinkButton } from 'components/ui/form';
|
||||||
import { COLOR_GREEN, COLOR_BLUE } from 'components/ui';
|
import { COLOR_GREEN, COLOR_BLUE } from 'components/ui';
|
||||||
import { restoreScroll } from 'components/ui/scroll/scroll';
|
|
||||||
import { ContactLink } from 'components/contact';
|
import { ContactLink } from 'components/contact';
|
||||||
|
|
||||||
import styles from './applicationsIndex.scss';
|
import styles from './applicationsIndex.scss';
|
||||||
@ -14,127 +13,23 @@ import messages from './ApplicationsIndex.intl.json';
|
|||||||
import cubeIcon from './icons/cube.svg';
|
import cubeIcon from './icons/cube.svg';
|
||||||
import loadingCubeIcon from './icons/loading-cube.svg';
|
import loadingCubeIcon from './icons/loading-cube.svg';
|
||||||
import toolsIcon from './icons/tools.svg';
|
import toolsIcon from './icons/tools.svg';
|
||||||
import ApplicationItem from './ApplicationItem';
|
import ApplicationsList from './list';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
clientId?: ?string,
|
clientId?: ?string,
|
||||||
displayForGuest: bool,
|
displayForGuest: bool,
|
||||||
applications: Array<OauthAppResponse>,
|
applications: Array<OauthAppResponse>,
|
||||||
isLoading: bool,
|
isLoading: bool,
|
||||||
deleteApp: (string) => Promise<any>,
|
deleteApp: string => Promise<any>,
|
||||||
resetApp: (string, bool) => Promise<any>,
|
resetApp: (string, bool) => Promise<any>
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
export default class ApplicationsIndex extends Component<Props> {
|
||||||
expandedApp: ?string,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class ApplicationsIndex extends Component<Props, State> {
|
|
||||||
state = {
|
|
||||||
expandedApp: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
appsRefs: {[key: string]: ?HTMLDivElement} = {};
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps: Props) {
|
|
||||||
const { applications, isLoading, clientId } = this.props;
|
|
||||||
|
|
||||||
if (isLoading !== prevProps.isLoading && applications.length) {
|
|
||||||
if (clientId && applications.some((app) => app.clientId === clientId)) {
|
|
||||||
requestAnimationFrame(() => this.onTileClick(clientId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { displayForGuest, applications, isLoading, resetApp, deleteApp } = this.props;
|
|
||||||
const { expandedApp } = this.state;
|
|
||||||
|
|
||||||
let content: Node;
|
|
||||||
|
|
||||||
if (displayForGuest) {
|
|
||||||
content = (
|
|
||||||
<div className={styles.emptyState}>
|
|
||||||
<img src={toolsIcon} className={styles.emptyStateIcon} />
|
|
||||||
<div className={styles.emptyStateText}>
|
|
||||||
<div>
|
|
||||||
<Message {...messages.weDontKnowAnythingAboutYou}/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Message {...messages.youMustAuthToBegin}/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<LinkButton
|
|
||||||
to="/login"
|
|
||||||
label={messages.authorization}
|
|
||||||
color={COLOR_BLUE}
|
|
||||||
className={styles.emptyStateActionButton}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else if (isLoading) {
|
|
||||||
content = (
|
|
||||||
<div className={styles.emptyState}>
|
|
||||||
<img src={loadingCubeIcon} className={styles.loadingStateIcon} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else if (applications.length > 0) {
|
|
||||||
content = (
|
|
||||||
<div>
|
|
||||||
<div className={styles.appsListTitleContainer}>
|
|
||||||
<div className={styles.appsListTitle}>
|
|
||||||
<Message {...messages.yourApplications} />
|
|
||||||
</div>
|
|
||||||
<LinkButton
|
|
||||||
to="/dev/applications/new"
|
|
||||||
label={messages.addNew}
|
|
||||||
color={COLOR_GREEN}
|
|
||||||
className={styles.appsListAddNewAppBtn}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className={styles.appsListContainer}>
|
|
||||||
{applications.map((app: OauthAppResponse) => (
|
|
||||||
<div key={app.clientId} ref={(elem) => {this.appsRefs[app.clientId] = elem;}}>
|
|
||||||
<ApplicationItem
|
|
||||||
application={app}
|
|
||||||
expand={app.clientId === expandedApp}
|
|
||||||
onTileClick={this.onTileClick}
|
|
||||||
onResetSubmit={resetApp}
|
|
||||||
onDeleteSubmit={deleteApp}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
content = (
|
|
||||||
<div className={styles.emptyState}>
|
|
||||||
<img src={cubeIcon} className={styles.emptyStateIcon} />
|
|
||||||
<div className={styles.emptyStateText}>
|
|
||||||
<div>
|
|
||||||
<Message {...messages.youDontHaveAnyApplication}/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Message {...messages.shallWeStart}/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<LinkButton
|
|
||||||
to="/dev/applications/new"
|
|
||||||
label={messages.addNew}
|
|
||||||
color={COLOR_GREEN}
|
|
||||||
className={styles.emptyStateActionButton}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div className={styles.welcomeContainer}>
|
<div className={styles.welcomeContainer}>
|
||||||
<Message {...messages.accountsForDevelopers} >
|
<Message {...messages.accountsForDevelopers}>
|
||||||
{(pageTitle: string) => (
|
{(pageTitle: string) => (
|
||||||
<h2 className={styles.welcomeTitle}>
|
<h2 className={styles.welcomeTitle}>
|
||||||
<Helmet title={pageTitle} />
|
<Helmet title={pageTitle} />
|
||||||
@ -144,38 +39,120 @@ export default class ApplicationsIndex extends Component<Props, State> {
|
|||||||
</Message>
|
</Message>
|
||||||
<div className={styles.welcomeTitleDelimiter} />
|
<div className={styles.welcomeTitleDelimiter} />
|
||||||
<div className={styles.welcomeParagraph}>
|
<div className={styles.welcomeParagraph}>
|
||||||
<Message {...messages.accountsAllowsYouYoUseOauth2} values={{
|
<Message
|
||||||
ourDocumentation: (
|
{...messages.accountsAllowsYouYoUseOauth2}
|
||||||
<a href="http://docs.ely.by/oauth.html" target="_blank">
|
values={{
|
||||||
<Message {...messages.ourDocumentation} />
|
ourDocumentation: (
|
||||||
</a>
|
<a
|
||||||
),
|
href="http://docs.ely.by/oauth.html"
|
||||||
}} />
|
target="_blank"
|
||||||
|
>
|
||||||
|
<Message
|
||||||
|
{...messages.ourDocumentation}
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.welcomeParagraph}>
|
<div className={styles.welcomeParagraph}>
|
||||||
<Message {...messages.ifYouHaveAnyTroubles} values={{
|
<Message
|
||||||
feedback: (
|
{...messages.ifYouHaveAnyTroubles}
|
||||||
<ContactLink>
|
values={{
|
||||||
<Message {...messages.feedback} />
|
feedback: (
|
||||||
</ContactLink>
|
<ContactLink>
|
||||||
),
|
<Message {...messages.feedback} />
|
||||||
}} />
|
</ContactLink>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{content}
|
{this.getContent()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onTileClick = (clientId: string) => {
|
getContent() {
|
||||||
const expandedApp = this.state.expandedApp === clientId ? null : clientId;
|
const {
|
||||||
|
displayForGuest,
|
||||||
|
applications,
|
||||||
|
isLoading,
|
||||||
|
resetApp,
|
||||||
|
deleteApp,
|
||||||
|
clientId
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
this.setState({expandedApp}, () => {
|
if (displayForGuest) {
|
||||||
if (expandedApp !== null) {
|
return <Guest />;
|
||||||
// TODO: @SleepWalker: мб у тебя есть идея, как это сделать более правильно и менее дёргано?
|
} else if (isLoading) {
|
||||||
setTimeout(() => restoreScroll(this.appsRefs[clientId]), 150);
|
return <Loader />;
|
||||||
}
|
} else if (applications.length > 0) {
|
||||||
});
|
return (
|
||||||
};
|
<ApplicationsList
|
||||||
|
applications={applications}
|
||||||
|
resetApp={resetApp}
|
||||||
|
deleteApp={deleteApp}
|
||||||
|
clientId={clientId}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <NoApps />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Loader() {
|
||||||
|
return (
|
||||||
|
<div className={styles.emptyState}>
|
||||||
|
<img src={loadingCubeIcon} className={styles.loadingStateIcon} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Guest() {
|
||||||
|
return (
|
||||||
|
<div className={styles.emptyState}>
|
||||||
|
<img src={toolsIcon} className={styles.emptyStateIcon} />
|
||||||
|
<div className={styles.emptyStateText}>
|
||||||
|
<div>
|
||||||
|
<Message {...messages.weDontKnowAnythingAboutYou} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Message {...messages.youMustAuthToBegin} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<LinkButton
|
||||||
|
to="/login"
|
||||||
|
label={messages.authorization}
|
||||||
|
color={COLOR_BLUE}
|
||||||
|
className={styles.emptyStateActionButton}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function NoApps() {
|
||||||
|
return (
|
||||||
|
<div className={styles.emptyState}>
|
||||||
|
<img src={cubeIcon} className={styles.emptyStateIcon} />
|
||||||
|
<div className={styles.emptyStateText}>
|
||||||
|
<div>
|
||||||
|
<Message {...messages.youDontHaveAnyApplication} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Message {...messages.shallWeStart} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<LinkButton
|
||||||
|
to="/dev/applications/new"
|
||||||
|
label={messages.addNew}
|
||||||
|
color={COLOR_GREEN}
|
||||||
|
className={styles.emptyStateActionButton}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -9,44 +9,162 @@ import { SKIN_LIGHT, COLOR_BLACK, COLOR_RED } from 'components/ui';
|
|||||||
import { Input, Button } from 'components/ui/form';
|
import { Input, Button } from 'components/ui/form';
|
||||||
import Collapse from 'components/ui/collapse';
|
import Collapse from 'components/ui/collapse';
|
||||||
|
|
||||||
import styles from './applicationsIndex.scss';
|
import styles from '../applicationsIndex.scss';
|
||||||
import messages from './ApplicationsIndex.intl.json';
|
import messages from '../ApplicationsIndex.intl.json';
|
||||||
|
|
||||||
const ACTION_REVOKE_TOKENS = 'revoke-tokens';
|
const ACTION_REVOKE_TOKENS = 'revoke-tokens';
|
||||||
const ACTION_RESET_SECRET = 'reset-secret';
|
const ACTION_RESET_SECRET = 'reset-secret';
|
||||||
const ACTION_DELETE = 'delete';
|
const ACTION_DELETE = 'delete';
|
||||||
|
const actionButtons = [
|
||||||
|
{
|
||||||
|
type: ACTION_REVOKE_TOKENS,
|
||||||
|
label: messages.revokeAllTokens
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ACTION_RESET_SECRET,
|
||||||
|
label: messages.resetClientSecret
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ACTION_DELETE,
|
||||||
|
label: messages.delete
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
export default class ApplicationItem extends Component<{
|
export default class ApplicationItem extends Component<
|
||||||
application: OauthAppResponse,
|
{
|
||||||
expand: bool,
|
application: OauthAppResponse,
|
||||||
onTileClick: (string) => void,
|
expand: bool,
|
||||||
onResetSubmit: (string, bool) => Promise<*>,
|
onTileClick: string => void,
|
||||||
onDeleteSubmit: (string) => Promise<*>,
|
onResetSubmit: (string, bool) => Promise<*>,
|
||||||
}, {
|
onDeleteSubmit: string => Promise<*>
|
||||||
selectedAction: ?string,
|
},
|
||||||
isActionPerforming: bool,
|
{
|
||||||
detailsHeight: number,
|
selectedAction: ?string,
|
||||||
}> {
|
isActionPerforming: bool,
|
||||||
|
detailsHeight: number
|
||||||
|
}
|
||||||
|
> {
|
||||||
state = {
|
state = {
|
||||||
selectedAction: null,
|
selectedAction: null,
|
||||||
isActionPerforming: false,
|
isActionPerforming: false,
|
||||||
detailsHeight: 0,
|
detailsHeight: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { application: app, expand } = this.props;
|
const { application: app, expand } = this.props;
|
||||||
const { selectedAction, isActionPerforming } = this.state;
|
const { selectedAction } = this.state;
|
||||||
|
|
||||||
let actionContent: Node;
|
return (
|
||||||
|
<div
|
||||||
|
className={classNames(styles.appItemContainer, {
|
||||||
|
[styles.appExpanded]: expand
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<div className={styles.appItemTile} onClick={this.onTileToggle}>
|
||||||
|
<div className={styles.appTileTitle}>
|
||||||
|
<div className={styles.appName}>{app.name}</div>
|
||||||
|
<div className={styles.appStats}>
|
||||||
|
Client ID: {app.clientId}
|
||||||
|
{typeof app.countUsers !== 'undefined' && (
|
||||||
|
<span>
|
||||||
|
{' | '}
|
||||||
|
<Message
|
||||||
|
{...messages.countUsers}
|
||||||
|
values={{
|
||||||
|
count: app.countUsers
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.appItemToggle}>
|
||||||
|
<div className={styles.appItemToggleIcon} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Collapse isOpened={expand} onRest={this.onCollapseRest}>
|
||||||
|
<div className={styles.appDetailsContainer}>
|
||||||
|
<div className={styles.appDetailsInfoField}>
|
||||||
|
<Link
|
||||||
|
to={`/dev/applications/${app.clientId}`}
|
||||||
|
className={styles.editAppLink}
|
||||||
|
>
|
||||||
|
<Message
|
||||||
|
{...messages.editDescription}
|
||||||
|
values={{
|
||||||
|
icon: (
|
||||||
|
<div
|
||||||
|
className={styles.pencilIcon}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
<Input
|
||||||
|
label="Client ID:"
|
||||||
|
skin={SKIN_LIGHT}
|
||||||
|
disabled
|
||||||
|
value={app.clientId}
|
||||||
|
copy
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.appDetailsInfoField}>
|
||||||
|
<Input
|
||||||
|
label="Client Secret:"
|
||||||
|
skin={SKIN_LIGHT}
|
||||||
|
disabled
|
||||||
|
value={app.clientSecret}
|
||||||
|
copy
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.appDetailsDescription}>
|
||||||
|
<Message
|
||||||
|
{...messages.ifYouSuspectingThatSecretHasBeenCompromised}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.appActionsButtons}>
|
||||||
|
{actionButtons.map(({ type, label }) => (
|
||||||
|
<Button
|
||||||
|
key={type}
|
||||||
|
label={label}
|
||||||
|
color={COLOR_BLACK}
|
||||||
|
className={styles.appActionButton}
|
||||||
|
disabled={
|
||||||
|
selectedAction
|
||||||
|
&& selectedAction !== type
|
||||||
|
}
|
||||||
|
onClick={this.onActionButtonClick(type)}
|
||||||
|
small
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{this.getActionContent()}
|
||||||
|
</div>
|
||||||
|
</Collapse>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getActionContent() {
|
||||||
|
const { selectedAction, isActionPerforming } = this.state;
|
||||||
|
|
||||||
switch (selectedAction) {
|
switch (selectedAction) {
|
||||||
case ACTION_REVOKE_TOKENS:
|
case ACTION_REVOKE_TOKENS:
|
||||||
case ACTION_RESET_SECRET:
|
case ACTION_RESET_SECRET:
|
||||||
actionContent = (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className={styles.appActionDescription}>
|
<div className={styles.appActionDescription}>
|
||||||
<Message {...messages.allRefreshTokensWillBecomeInvalid} />{' '}
|
<Message
|
||||||
<Message {...messages.takeCareAccessTokensInvalidation} />
|
{...messages.allRefreshTokensWillBecomeInvalid}
|
||||||
|
/>{' '}
|
||||||
|
<Message
|
||||||
|
{...messages.takeCareAccessTokensInvalidation}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.appActionsButtons}>
|
<div className={styles.appActionsButtons}>
|
||||||
<Button
|
<Button
|
||||||
@ -64,7 +182,10 @@ export default class ApplicationItem extends Component<{
|
|||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
className={styles.continueActionLink}
|
className={styles.continueActionLink}
|
||||||
onClick={this.onResetSubmit(selectedAction === ACTION_RESET_SECRET)}
|
onClick={this.onResetSubmit(
|
||||||
|
selectedAction
|
||||||
|
=== ACTION_RESET_SECRET
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<Message {...messages.continue} />
|
<Message {...messages.continue} />
|
||||||
</div>
|
</div>
|
||||||
@ -73,13 +194,17 @@ export default class ApplicationItem extends Component<{
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
break;
|
|
||||||
case ACTION_DELETE:
|
case ACTION_DELETE:
|
||||||
actionContent = (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className={styles.appActionDescription}>
|
<div className={styles.appActionDescription}>
|
||||||
<Message {...messages.appAndAllTokenWillBeDeleted} />{' '}
|
<Message
|
||||||
<Message {...messages.takeCareAccessTokensInvalidation} />
|
{...messages.appAndAllTokenWillBeDeleted}
|
||||||
|
/>{' '}
|
||||||
|
<Message
|
||||||
|
{...messages.takeCareAccessTokensInvalidation}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.appActionsButtons}>
|
<div className={styles.appActionsButtons}>
|
||||||
<Button
|
<Button
|
||||||
@ -107,100 +232,10 @@ export default class ApplicationItem extends Component<{
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
actionContent = null;
|
return null;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={classNames(styles.appItemContainer, {
|
|
||||||
[styles.appExpanded]: expand,
|
|
||||||
})}>
|
|
||||||
<div className={styles.appItemTile} onClick={this.onTileToggle}>
|
|
||||||
<div className={styles.appTileTitle}>
|
|
||||||
<div className={styles.appName}>
|
|
||||||
{app.name}
|
|
||||||
</div>
|
|
||||||
<div className={styles.appStats}>
|
|
||||||
Client ID: {app.clientId}
|
|
||||||
{typeof app.countUsers !== 'undefined' && (
|
|
||||||
<span>
|
|
||||||
{' | '}
|
|
||||||
<Message {...messages.countUsers} values={{
|
|
||||||
count: app.countUsers,
|
|
||||||
}} />
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className={styles.appItemToggle}>
|
|
||||||
<div className={styles.appItemToggleIcon} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Collapse isOpened={expand} onRest={this.onCollapseRest}>
|
|
||||||
<div className={styles.appDetailsContainer}>
|
|
||||||
<div className={styles.appDetailsInfoField}>
|
|
||||||
<Link to={`/dev/applications/${app.clientId}`} className={styles.editAppLink}>
|
|
||||||
<Message {...messages.editDescription} values={{
|
|
||||||
icon: <div className={styles.pencilIcon} />,
|
|
||||||
}} />
|
|
||||||
</Link>
|
|
||||||
<Input
|
|
||||||
label="Client ID:"
|
|
||||||
skin={SKIN_LIGHT}
|
|
||||||
disabled
|
|
||||||
value={app.clientId}
|
|
||||||
copy
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.appDetailsInfoField}>
|
|
||||||
<Input
|
|
||||||
label="Client Secret:"
|
|
||||||
skin={SKIN_LIGHT}
|
|
||||||
disabled
|
|
||||||
value={app.clientSecret}
|
|
||||||
copy
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.appDetailsDescription}>
|
|
||||||
<Message {...messages.ifYouSuspectingThatSecretHasBeenCompromised} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.appActionsButtons}>
|
|
||||||
<Button
|
|
||||||
label={messages.revokeAllTokens}
|
|
||||||
color={COLOR_BLACK}
|
|
||||||
className={styles.appActionButton}
|
|
||||||
disabled={selectedAction && selectedAction !== ACTION_REVOKE_TOKENS}
|
|
||||||
onClick={this.onActionButtonClick(ACTION_REVOKE_TOKENS)}
|
|
||||||
small
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
label={messages.resetClientSecret}
|
|
||||||
color={COLOR_BLACK}
|
|
||||||
className={styles.appActionButton}
|
|
||||||
disabled={selectedAction && selectedAction !== ACTION_RESET_SECRET}
|
|
||||||
onClick={this.onActionButtonClick(ACTION_RESET_SECRET)}
|
|
||||||
small
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
label={messages.delete}
|
|
||||||
color={COLOR_BLACK}
|
|
||||||
className={styles.appActionButton}
|
|
||||||
disabled={selectedAction && selectedAction !== ACTION_DELETE}
|
|
||||||
onClick={this.onActionButtonClick(ACTION_DELETE)}
|
|
||||||
small
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{actionContent}
|
|
||||||
</div>
|
|
||||||
</Collapse>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onTileToggle = () => {
|
onTileToggle = () => {
|
||||||
@ -211,33 +246,33 @@ export default class ApplicationItem extends Component<{
|
|||||||
onCollapseRest = () => {
|
onCollapseRest = () => {
|
||||||
if (!this.props.expand && this.state.selectedAction) {
|
if (!this.props.expand && this.state.selectedAction) {
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedAction: null,
|
selectedAction: null
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onActionButtonClick = (type: ?string) => () => {
|
onActionButtonClick = (type: ?string) => () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedAction: type === this.state.selectedAction ? null : type,
|
selectedAction: type === this.state.selectedAction ? null : type
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onResetSubmit = (resetClientSecret: bool) => async () => {
|
onResetSubmit = (resetClientSecret: bool) => async () => {
|
||||||
const { onResetSubmit, application } = this.props;
|
const { onResetSubmit, application } = this.props;
|
||||||
this.setState({
|
this.setState({
|
||||||
isActionPerforming: true,
|
isActionPerforming: true
|
||||||
});
|
});
|
||||||
await onResetSubmit(application.clientId, resetClientSecret);
|
await onResetSubmit(application.clientId, resetClientSecret);
|
||||||
this.setState({
|
this.setState({
|
||||||
isActionPerforming: false,
|
isActionPerforming: false,
|
||||||
selectedAction: null,
|
selectedAction: null
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onSubmitDelete = () => {
|
onSubmitDelete = () => {
|
||||||
const { onDeleteSubmit, application } = this.props;
|
const { onDeleteSubmit, application } = this.props;
|
||||||
this.setState({
|
this.setState({
|
||||||
isActionPerforming: true,
|
isActionPerforming: true
|
||||||
});
|
});
|
||||||
onDeleteSubmit(application.clientId);
|
onDeleteSubmit(application.clientId);
|
||||||
};
|
};
|
102
src/components/dev/apps/list/ApplicationsList.js
Normal file
102
src/components/dev/apps/list/ApplicationsList.js
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
// @flow
|
||||||
|
import type { OauthAppResponse } from 'services/api/oauth';
|
||||||
|
import React from 'react';
|
||||||
|
import { restoreScroll } from 'components/ui/scroll/scroll';
|
||||||
|
import { FormattedMessage as Message } from 'react-intl';
|
||||||
|
import { LinkButton } from 'components/ui/form';
|
||||||
|
import { COLOR_GREEN } from 'components/ui';
|
||||||
|
|
||||||
|
import messages from '../ApplicationsIndex.intl.json';
|
||||||
|
import styles from '../applicationsIndex.scss';
|
||||||
|
import ApplicationItem from './ApplicationItem';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
applications: Array<OauthAppResponse>,
|
||||||
|
deleteApp: string => Promise<any>,
|
||||||
|
resetApp: (string, bool) => Promise<any>,
|
||||||
|
clientId: ?string
|
||||||
|
};
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
expandedApp: ?string
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class ApplicationsList extends React.Component<Props, State> {
|
||||||
|
state = {
|
||||||
|
expandedApp: null
|
||||||
|
};
|
||||||
|
|
||||||
|
appsRefs: { [key: string]: ?HTMLDivElement } = {};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.checkForActiveApp();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate() {
|
||||||
|
this.checkForActiveApp();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { applications, resetApp, deleteApp } = this.props;
|
||||||
|
const { expandedApp } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className={styles.appsListTitleContainer}>
|
||||||
|
<div className={styles.appsListTitle}>
|
||||||
|
<Message {...messages.yourApplications} />
|
||||||
|
</div>
|
||||||
|
<LinkButton
|
||||||
|
to="/dev/applications/new"
|
||||||
|
label={messages.addNew}
|
||||||
|
color={COLOR_GREEN}
|
||||||
|
className={styles.appsListAddNewAppBtn}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={styles.appsListContainer}>
|
||||||
|
{applications.map((app) => (
|
||||||
|
<div
|
||||||
|
key={app.clientId}
|
||||||
|
ref={(elem) => {
|
||||||
|
this.appsRefs[app.clientId] = elem;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ApplicationItem
|
||||||
|
application={app}
|
||||||
|
expand={app.clientId === expandedApp}
|
||||||
|
onTileClick={this.onTileClick}
|
||||||
|
onResetSubmit={resetApp}
|
||||||
|
onDeleteSubmit={deleteApp}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
checkForActiveApp() {
|
||||||
|
const { applications, clientId } = this.props;
|
||||||
|
const { expandedApp } = this.state;
|
||||||
|
|
||||||
|
if (
|
||||||
|
clientId
|
||||||
|
&& expandedApp !== clientId
|
||||||
|
&& applications.some((app) => app.clientId === clientId)
|
||||||
|
) {
|
||||||
|
requestAnimationFrame(() => this.onTileClick(clientId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onTileClick = (clientId: string) => {
|
||||||
|
const expandedApp
|
||||||
|
= this.state.expandedApp === clientId ? null : clientId;
|
||||||
|
|
||||||
|
this.setState({ expandedApp }, () => {
|
||||||
|
if (expandedApp !== null) {
|
||||||
|
// TODO: @SleepWalker: мб у тебя есть идея, как это сделать более правильно и менее дёргано?
|
||||||
|
setTimeout(() => restoreScroll(this.appsRefs[clientId]), 150);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
3
src/components/dev/apps/list/index.js
Normal file
3
src/components/dev/apps/list/index.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
export { default } from './ApplicationsList';
|
Loading…
Reference in New Issue
Block a user