mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-28 18:26:54 +00:00
fix(client): put token widget behind email check (#63910)
This commit is contained in:
@@ -1314,7 +1314,8 @@
|
||||
"copied": "Token copied to clipboard",
|
||||
"copy-error": "Error copying token to clipboard",
|
||||
"token-usage": "Your Exam Environment authorization token is used to log you into the desktop application.",
|
||||
"generated": "A new Exam Environment authorization token has been generated for your account."
|
||||
"generated": "A new Exam Environment authorization token has been generated for your account.",
|
||||
"non-staff-testing": "Only freeCodeCamp staff are allowed to generate exam tokens on non-production environments at this time."
|
||||
},
|
||||
"shortcuts": {
|
||||
"title": "Keyboard shortcuts",
|
||||
|
||||
@@ -6,7 +6,6 @@ import { createSelector } from 'reselect';
|
||||
import { scroller } from 'react-scroll';
|
||||
|
||||
import { Container, Spacer } from '@freecodecamp/ui';
|
||||
import { useFeatureIsOn } from '@growthbook/growthbook-react';
|
||||
|
||||
import store from 'store';
|
||||
import envData from '../../config/env.json';
|
||||
@@ -108,8 +107,6 @@ export function ShowSettings(props: ShowSettingsProps): JSX.Element {
|
||||
|
||||
const isSignedInRef = useRef(isSignedIn);
|
||||
|
||||
const examTokenFlag = useFeatureIsOn('exam-token-widget');
|
||||
|
||||
const handleHashChange = () => {
|
||||
const id = window.location.hash.replace('#', '');
|
||||
if (id) {
|
||||
@@ -205,7 +202,7 @@ export function ShowSettings(props: ShowSettingsProps): JSX.Element {
|
||||
<Spacer size='m' />
|
||||
<Honesty isHonest={isHonest} updateIsHonest={updateIsHonest} />
|
||||
<Spacer size='m' />
|
||||
{examTokenFlag && <ExamToken />}
|
||||
<ExamToken email={email} />
|
||||
<Certification
|
||||
completedChallenges={completedChallenges}
|
||||
createFlashMessage={createFlashMessage}
|
||||
|
||||
@@ -3,8 +3,15 @@ import { Button, Panel, Modal, Spacer } from '@freecodecamp/ui';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FullWidthRow } from '../helpers';
|
||||
import { generateExamToken } from '../../utils/ajax';
|
||||
import envData from '../../../config/env.json';
|
||||
|
||||
function ExamToken(): JSX.Element {
|
||||
const { deploymentEnv } = envData;
|
||||
|
||||
interface ExamTokenProps {
|
||||
email: string;
|
||||
}
|
||||
|
||||
function ExamToken({ email }: ExamTokenProps) {
|
||||
const [examToken, setExamToken] = useState<string | null>(null);
|
||||
const [examTokenError, setExamTokenError] = useState<string | null>(null);
|
||||
|
||||
@@ -32,6 +39,9 @@ function ExamToken(): JSX.Element {
|
||||
setTimeout(() => setRecentlyGenerated(false), 10000);
|
||||
};
|
||||
|
||||
const nonStaffTesting =
|
||||
deploymentEnv !== 'production' && !email.endsWith('@freecodecamp.org');
|
||||
|
||||
return (
|
||||
<FullWidthRow>
|
||||
<Modal
|
||||
@@ -90,9 +100,10 @@ function ExamToken(): JSX.Element {
|
||||
<p>{t('exam-token.note')}</p>
|
||||
<strong>{t('exam-token.invalidation-2')}</strong>
|
||||
<Spacer size='s' />
|
||||
{nonStaffTesting && <p>{t('exam-token.non-staff-testing')}</p>}
|
||||
<Button
|
||||
block={true}
|
||||
disabled={recentlyGenerated}
|
||||
disabled={recentlyGenerated || nonStaffTesting}
|
||||
onClick={() => void getToken()}
|
||||
>
|
||||
{t('exam-token.generate-exam-token')}
|
||||
|
||||
@@ -4,8 +4,17 @@ import { Button, Spacer } from '@freecodecamp/ui';
|
||||
|
||||
import { examEnvironmentAuthorizationTokenApi } from '../../../utils/ajax';
|
||||
import { Loader } from '../../../components/helpers';
|
||||
import envData from '../../../../config/env.json';
|
||||
|
||||
export function ExamTokenControls(): JSX.Element {
|
||||
const { deploymentEnv } = envData;
|
||||
|
||||
interface ExamTokenControlsProps {
|
||||
email: string;
|
||||
}
|
||||
|
||||
export function ExamTokenControls({
|
||||
email
|
||||
}: ExamTokenControlsProps): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [copySuccess, setCopySuccess] = useState<string | null>(null);
|
||||
@@ -33,6 +42,9 @@ export function ExamTokenControls(): JSX.Element {
|
||||
);
|
||||
}
|
||||
|
||||
const nonStaffTesting =
|
||||
deploymentEnv !== 'production' && !email.endsWith('@freecodecamp.org');
|
||||
|
||||
return (
|
||||
<>
|
||||
<h3>{t('exam-token.exam-token')}</h3>
|
||||
@@ -60,6 +72,7 @@ export function ExamTokenControls(): JSX.Element {
|
||||
{t('exam-token.no-token')}
|
||||
</p>
|
||||
)}
|
||||
{nonStaffTesting && <p>{t('exam-token.non-staff-testing')}</p>}
|
||||
{generateMutation.isLoading || getTokenQuery.isLoading ? (
|
||||
<Button block={true}>
|
||||
<Loader />
|
||||
@@ -67,7 +80,11 @@ export function ExamTokenControls(): JSX.Element {
|
||||
) : (
|
||||
<Button
|
||||
block={true}
|
||||
disabled={generateMutation.isLoading || getTokenQuery.isLoading}
|
||||
disabled={
|
||||
generateMutation.isLoading ||
|
||||
getTokenQuery.isLoading ||
|
||||
nonStaffTesting
|
||||
}
|
||||
onClick={() => void generateToken()}
|
||||
>
|
||||
{t('exam-token.generate-exam-token')}
|
||||
|
||||
@@ -19,10 +19,15 @@ import { connect } from 'react-redux';
|
||||
import LearnLayout from '../../../components/layouts/learn';
|
||||
import ChallengeTitle from '../components/challenge-title';
|
||||
import useDetectOS from '../utils/use-detect-os';
|
||||
import { ChallengeNode, CompletedChallenge } from '../../../redux/prop-types';
|
||||
import {
|
||||
ChallengeNode,
|
||||
CompletedChallenge,
|
||||
User
|
||||
} from '../../../redux/prop-types';
|
||||
import {
|
||||
completedChallengesSelector,
|
||||
isSignedInSelector
|
||||
isSignedInSelector,
|
||||
userSelector
|
||||
} from '../../../redux/selectors';
|
||||
import { examAttempts } from '../../../utils/ajax';
|
||||
import MissingPrerequisites from '../exam/components/missing-prerequisites';
|
||||
@@ -43,14 +48,17 @@ const mapStateToProps = createSelector(
|
||||
completedChallengesSelector,
|
||||
isChallengeCompletedSelector,
|
||||
isSignedInSelector,
|
||||
userSelector,
|
||||
(
|
||||
completedChallenges: CompletedChallenge[],
|
||||
isChallengeCompleted: boolean,
|
||||
isSignedIn: boolean
|
||||
isSignedIn: boolean,
|
||||
user: User | null
|
||||
) => ({
|
||||
completedChallenges,
|
||||
isChallengeCompleted,
|
||||
isSignedIn
|
||||
isSignedIn,
|
||||
user
|
||||
})
|
||||
);
|
||||
|
||||
@@ -62,6 +70,7 @@ interface ShowExamDownloadProps {
|
||||
completedChallenges: CompletedChallenge[];
|
||||
isChallengeCompleted: boolean;
|
||||
isSignedIn: boolean;
|
||||
user: User | null;
|
||||
}
|
||||
|
||||
function ShowExamDownload({
|
||||
@@ -73,7 +82,8 @@ function ShowExamDownload({
|
||||
},
|
||||
completedChallenges,
|
||||
isChallengeCompleted,
|
||||
isSignedIn
|
||||
isSignedIn,
|
||||
user
|
||||
}: ShowExamDownloadProps): JSX.Element {
|
||||
const [latestVersion, setLatestVersion] = useState<string | null>(null);
|
||||
|
||||
@@ -217,7 +227,7 @@ function ShowExamDownload({
|
||||
<h2>{t('exam.attempts')}</h2>
|
||||
<Attempts examChallengeId={id} />
|
||||
<Spacer size='l' />
|
||||
<ExamTokenControls />
|
||||
<ExamTokenControls email={user!.email} />
|
||||
</>
|
||||
)}
|
||||
<p>
|
||||
|
||||
Reference in New Issue
Block a user