mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-28 18:26:54 +00:00
feat(client): add 3 disclosure cde for rdb (#62178)
Co-authored-by: Ahmad Abdolsaheb <ahmad.abdolsaheb@gmail.com>
This commit is contained in:
@@ -5,6 +5,15 @@
|
||||
"aa-test-in-component": {
|
||||
"defaultValue": false
|
||||
},
|
||||
"rdb-codespaces-instructions": {
|
||||
"defaultValue": true
|
||||
},
|
||||
"rdb-local-instructions": {
|
||||
"defaultValue": true
|
||||
},
|
||||
"rdb-ona-instructions": {
|
||||
"defaultValue": true
|
||||
},
|
||||
"landing-top-skill-focused": {
|
||||
"defaultValue": false,
|
||||
"rules": [
|
||||
@@ -13,8 +22,14 @@
|
||||
"hashAttribute": "id",
|
||||
"seed": "landing-top-skill-focused",
|
||||
"hashVersion": 2,
|
||||
"variations": [false, true],
|
||||
"weights": [0.5, 0.5],
|
||||
"variations": [
|
||||
false,
|
||||
true
|
||||
],
|
||||
"weights": [
|
||||
0.5,
|
||||
0.5
|
||||
],
|
||||
"key": "landing-top-skill-focused",
|
||||
"meta": [
|
||||
{
|
||||
@@ -31,7 +46,7 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"replace-20-with-25": {
|
||||
"replace-20-with-25": {
|
||||
"defaultValue": false,
|
||||
"rules": [
|
||||
{
|
||||
@@ -39,8 +54,14 @@
|
||||
"hashAttribute": "id",
|
||||
"seed": "replace-20-with-25",
|
||||
"hashVersion": 2,
|
||||
"variations": [false, true],
|
||||
"weights": [0.5, 0.5],
|
||||
"variations": [
|
||||
false,
|
||||
true
|
||||
],
|
||||
"weights": [
|
||||
0.5,
|
||||
0.5
|
||||
],
|
||||
"key": "replace-20-with-25",
|
||||
"meta": [
|
||||
{
|
||||
|
||||
@@ -533,6 +533,26 @@
|
||||
"project-preview-title": "Here's a preview of what you will build",
|
||||
"demo-project-title": "Here's an example of a project that meets the requirements",
|
||||
"github-required": "<0>Create a GitHub</0> account if you don't have one. You'll need it when you create the virtual Linux server machine. This process may take a few minutes.",
|
||||
"codespaces": {
|
||||
"intro": "This course runs in a virtual Linux machine using GitHub Codespaces. Follow these instructions to start the course:",
|
||||
"step-1": "<0>Create an GitHub account</0> if you don't have one",
|
||||
"step-2": "Click the start button below",
|
||||
"step-3": "On that page, click the create button",
|
||||
"step-4": "Once the virtual Linux machine is finished loading, start the CodeRoad extension by:",
|
||||
"step-5": "Clicking the \"hamburger\" menu near the top left of the VSCode window,",
|
||||
"step-6": "Going to the <0>View</0> menu,",
|
||||
"step-7": "Clicking on the <0>Command Palette</0> option,",
|
||||
"step-8": "and running the <0>CodeRoad: Start</0> command",
|
||||
"step-9": "Follow the instructions in CodeRoad to complete the course",
|
||||
"continue-project": "Clicking the button below will start a new project. If you have previously started the {{title}} course, go to the <0>repository page</0> to re-open a previous workspace.",
|
||||
"learn-more": "Learn more about <0>Codespace workspaces</0>.",
|
||||
"logout-warning": "If you log out of freeCodeCamp before you complete the entire {{course}} course, your progress will not be saved to your freeCodeCamp account.",
|
||||
"sub-step-3": "Navigate to your <0>Codespaces secrets page</0>",
|
||||
"sub-step-4": "Create a new secret named <0>CODEROAD_WEBHOOK_TOKEN</0>",
|
||||
"sub-step-5": "In the <0>Value</0> field, paste your token",
|
||||
"sub-step-6": "In the <0>Repository access</0> field, select the <1>freeCodeCamp/rdb-alpha</1> repository",
|
||||
"summary": "Codespaces Setup"
|
||||
},
|
||||
"ona": {
|
||||
"intro": "This course runs in a virtual Linux machine using Ona. Follow these instructions to start the course:",
|
||||
"step-1": "<0>Create an Ona account</0> if you don't have one",
|
||||
@@ -549,7 +569,8 @@
|
||||
"logout-warning": "If you log out of freeCodeCamp before you complete the entire {{course}} course, your progress will not be saved to your freeCodeCamp account.",
|
||||
"sub-step-3": "Navigate to your <0>Ona secrets page</0>",
|
||||
"sub-step-4": "Create a new secret named <0>CODEROAD_WEBHOOK_TOKEN</0>",
|
||||
"sub-step-5": "In the <0>Secret</0> field, paste your token"
|
||||
"sub-step-5": "In the <0>Secret</0> field, paste your token",
|
||||
"summary": "Ona Setup"
|
||||
},
|
||||
"local": {
|
||||
"intro": "This course runs in a virtual Linux machine on your computer. To run the course, you first need to download each of the following if you don't already have them:",
|
||||
@@ -571,7 +592,8 @@
|
||||
"step-7": "Copy the course URL below, paste it in the URL input, and click \"Load\".",
|
||||
"copy-url": "Copy Course URL",
|
||||
"step-8": "Click \"Start\" to begin.",
|
||||
"step-9": "Follow the instructions in CodeRoad to complete the course. Note: You may need to restart the terminal once for terminal settings to take effect and the tests to pass."
|
||||
"step-9": "Follow the instructions in CodeRoad to complete the course. Note: You may need to restart the terminal once for terminal settings to take effect and the tests to pass.",
|
||||
"summary": "Local Setup"
|
||||
},
|
||||
"step-1": "Step 1: Complete the project",
|
||||
"step-2": "Step 2: Submit your code",
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
import React from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { Spacer, Button, Callout } from '@freecodecamp/ui';
|
||||
|
||||
import { CodeAllyButton } from '../../../components/growth-book/codeally-button';
|
||||
|
||||
interface CodespacesInstructionsProps {
|
||||
challengeType: number;
|
||||
copyUrl: () => void;
|
||||
copyUserToken: () => void;
|
||||
generateUserToken: () => Promise<void>;
|
||||
isSignedIn: boolean;
|
||||
title: string;
|
||||
userToken: string | null;
|
||||
}
|
||||
|
||||
export function CodespacesInstructions({
|
||||
challengeType,
|
||||
copyUrl,
|
||||
copyUserToken,
|
||||
generateUserToken,
|
||||
isSignedIn,
|
||||
title,
|
||||
userToken
|
||||
}: CodespacesInstructionsProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
function openCodespaces() {
|
||||
const codespacesUrl = `https://codespaces.new/freeCodeCamp/rdb-alpha`;
|
||||
|
||||
window.open(codespacesUrl, '_blank');
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='ca-description'>
|
||||
<p>{t('learn.codespaces.intro')}</p>
|
||||
<ol>
|
||||
<li>
|
||||
<Trans i18nKey='learn.codespaces.step-1'>
|
||||
<a
|
||||
href='https://github.com/signup'
|
||||
rel='noopener noreferrer'
|
||||
target='_blank'
|
||||
>
|
||||
placeholder
|
||||
</a>
|
||||
</Trans>
|
||||
</li>
|
||||
{isSignedIn && (
|
||||
<>
|
||||
<Spacer size='s' />
|
||||
<p>{t('learn.local.sub-step-heading')}</p>
|
||||
<ol>
|
||||
<li>{t('learn.local.sub-step-1')}</li>
|
||||
<Spacer size='xxs' />
|
||||
<Button
|
||||
disabled={!!userToken}
|
||||
block={true}
|
||||
onClick={() => void generateUserToken()}
|
||||
>
|
||||
{t('learn.local.generate-token-btn')}
|
||||
</Button>
|
||||
<Spacer size='xs' />
|
||||
<li>{t('learn.local.sub-step-2')}</li>
|
||||
<Spacer size='xxs' />
|
||||
<Button
|
||||
disabled={!userToken}
|
||||
block={true}
|
||||
onClick={copyUserToken}
|
||||
>
|
||||
{t('learn.local.copy-token-btn')}
|
||||
</Button>
|
||||
<Spacer size='xs' />
|
||||
<li>
|
||||
<Trans i18nKey='learn.codespaces.sub-step-3'>
|
||||
<a
|
||||
href='https://github.com/settings/codespaces/secrets/new'
|
||||
rel='noopener noreferrer'
|
||||
target='_blank'
|
||||
>
|
||||
Codespaces secrets page
|
||||
</a>
|
||||
</Trans>
|
||||
</li>
|
||||
<li>
|
||||
<Trans i18nKey='learn.codespaces.sub-step-4'>
|
||||
<code>placeholder</code>
|
||||
</Trans>
|
||||
</li>
|
||||
<li>
|
||||
<Trans i18nKey='learn.codespaces.sub-step-5'>
|
||||
<code>placeholder</code>
|
||||
</Trans>
|
||||
</li>
|
||||
<li>
|
||||
<Trans i18nKey='learn.codespaces.sub-step-6'>
|
||||
<code>placeholder</code>
|
||||
<code>placeholder</code>
|
||||
</Trans>
|
||||
</li>
|
||||
</ol>
|
||||
<Spacer size='s' />
|
||||
</>
|
||||
)}
|
||||
<li>{t('learn.codespaces.step-2')}</li>
|
||||
<li>{t('learn.codespaces.step-3')}</li>
|
||||
<li>
|
||||
{t('learn.codespaces.step-4')}
|
||||
<ul>
|
||||
<li>{t('learn.codespaces.step-5')}</li>
|
||||
<li>
|
||||
<Trans i18nKey='learn.codespaces.step-6'>
|
||||
<code>placeholder</code>
|
||||
</Trans>
|
||||
</li>
|
||||
<li>
|
||||
<Trans i18nKey='learn.codespaces.step-7'>
|
||||
<code>placeholder</code>
|
||||
</Trans>
|
||||
</li>
|
||||
<li>
|
||||
<Trans i18nKey='learn.codespaces.step-8'>
|
||||
<code>placeholder</code>
|
||||
</Trans>
|
||||
</li>
|
||||
<li>{t('learn.local.step-6')}</li>
|
||||
<li>{t('learn.local.step-7')}</li>
|
||||
<Spacer size='xxs' />
|
||||
<Button block={true} onClick={copyUrl}>
|
||||
{t('learn.local.copy-url')}
|
||||
</Button>
|
||||
<Spacer size='xs' />
|
||||
<li>{t('learn.local.step-8')}</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>{t('learn.codespaces.step-9')}</li>
|
||||
</ol>
|
||||
<Spacer size='m' />
|
||||
<CodespacesContinueAlert title={title} />
|
||||
{isSignedIn && <CodespacesLogoutAlert course={title} />}
|
||||
<CodeAllyButton challengeType={challengeType} onClick={openCodespaces} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface CodespacesContinueAlertProps {
|
||||
title: string;
|
||||
}
|
||||
|
||||
function CodespacesContinueAlert({ title }: CodespacesContinueAlertProps) {
|
||||
return (
|
||||
<Callout variant='info'>
|
||||
<Trans values={{ title }} i18nKey='learn.codespaces.continue-project'>
|
||||
<a
|
||||
href='https://github.com/freeCodeCamp/rdb-alpha'
|
||||
rel='noopener noreferrer'
|
||||
target='_blank'
|
||||
>
|
||||
placeholder
|
||||
</a>
|
||||
</Trans>
|
||||
<Spacer size='m' />
|
||||
<Trans i18nKey='learn.codespaces.learn-more'>
|
||||
<a
|
||||
href='https://forum.freecodecamp.org/t/relational-database-curriculum-in-codespaces/761449'
|
||||
rel='noopener noreferrer'
|
||||
target='_blank'
|
||||
>
|
||||
placeholder
|
||||
</a>
|
||||
</Trans>
|
||||
</Callout>
|
||||
);
|
||||
}
|
||||
|
||||
interface CodespacesLogoutAlertProps {
|
||||
course: string;
|
||||
}
|
||||
|
||||
function CodespacesLogoutAlert({
|
||||
course
|
||||
}: CodespacesLogoutAlertProps): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Callout variant='danger'>
|
||||
{t('learn.codespaces.logout-warning', { course })}
|
||||
</Callout>
|
||||
);
|
||||
}
|
||||
+12
-93
@@ -1,103 +1,29 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { Spacer, Button } from '@freecodecamp/ui';
|
||||
import { postUserToken } from '../../../utils/ajax';
|
||||
import { createFlashMessage } from '../../../components/Flash/redux';
|
||||
import { FlashMessages } from '../../../components/Flash/redux/flash-messages';
|
||||
|
||||
import {
|
||||
isSignedInSelector,
|
||||
userTokenSelector
|
||||
} from '../../../redux/selectors';
|
||||
import { updateUserToken } from '../../../redux/actions';
|
||||
import { Link } from '../../../components/helpers';
|
||||
|
||||
import RdbLocalLogoutAlert from './rdb-local-logout-alert';
|
||||
|
||||
const mapStateToProps = (state: unknown) => ({
|
||||
isSignedIn: isSignedInSelector(state),
|
||||
userToken: userTokenSelector(state) as string | null
|
||||
});
|
||||
|
||||
const mapDispatchToProps = {
|
||||
createFlashMessage,
|
||||
updateUserToken
|
||||
};
|
||||
|
||||
interface RdbLocalInstructionsProps {
|
||||
course: string;
|
||||
createFlashMessage: typeof createFlashMessage;
|
||||
interface LocalInstructionsProps {
|
||||
copyUrl: () => void;
|
||||
copyUserToken: () => void;
|
||||
generateUserToken: () => Promise<void>;
|
||||
isSignedIn: boolean;
|
||||
updateUserToken: (arg0: string) => void;
|
||||
url: string;
|
||||
title: string;
|
||||
userToken: string | null;
|
||||
}
|
||||
|
||||
function RdbLocalInstructions({
|
||||
course,
|
||||
createFlashMessage,
|
||||
export function LocalInstructions({
|
||||
copyUrl,
|
||||
copyUserToken,
|
||||
generateUserToken,
|
||||
isSignedIn,
|
||||
updateUserToken,
|
||||
url,
|
||||
title,
|
||||
userToken
|
||||
}: RdbLocalInstructionsProps): JSX.Element {
|
||||
}: LocalInstructionsProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const coderoadTutorial = `https://raw.githubusercontent.com/${url}/main/tutorial.json`;
|
||||
|
||||
const generateUserToken = async () => {
|
||||
const createUserTokenResponse = await postUserToken();
|
||||
const { data = { userToken: null } } = createUserTokenResponse;
|
||||
|
||||
if (data?.userToken) {
|
||||
updateUserToken(data.userToken);
|
||||
createFlashMessage({
|
||||
type: 'success',
|
||||
message: FlashMessages.UserTokenGenerated
|
||||
});
|
||||
} else {
|
||||
createFlashMessage({
|
||||
type: 'danger',
|
||||
message: FlashMessages.UserTokenGenerateError
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const copyUserToken = () => {
|
||||
navigator.clipboard.writeText(userToken ?? '').then(
|
||||
() => {
|
||||
createFlashMessage({
|
||||
type: 'success',
|
||||
message: FlashMessages.UserTokenCopied
|
||||
});
|
||||
},
|
||||
() => {
|
||||
createFlashMessage({
|
||||
type: 'danger',
|
||||
message: FlashMessages.UserTokenCopyError
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const copyUrl = () => {
|
||||
navigator.clipboard.writeText(coderoadTutorial ?? '').then(
|
||||
() => {
|
||||
createFlashMessage({
|
||||
type: 'success',
|
||||
message: FlashMessages.CourseUrlCopied
|
||||
});
|
||||
},
|
||||
() => {
|
||||
createFlashMessage({
|
||||
type: 'danger',
|
||||
message: FlashMessages.CourseUrlCopyError
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='ca-description'>
|
||||
<p>{t('learn.local.intro')}</p>
|
||||
@@ -174,7 +100,7 @@ function RdbLocalInstructions({
|
||||
</Trans>
|
||||
</li>
|
||||
<Spacer size='xs' />
|
||||
<RdbLocalLogoutAlert course={course} />
|
||||
<RdbLocalLogoutAlert title={title} />
|
||||
</ol>
|
||||
<Spacer size='s' />
|
||||
</>
|
||||
@@ -203,10 +129,3 @@ function RdbLocalInstructions({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
RdbLocalInstructions.displayName = 'RdbLocalInstructions';
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(RdbLocalInstructions);
|
||||
+27
-89
@@ -1,101 +1,40 @@
|
||||
import React from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { connect } from 'react-redux';
|
||||
import { Spacer, Button } from '@freecodecamp/ui';
|
||||
import { postUserToken } from '../../../utils/ajax';
|
||||
import { createFlashMessage } from '../../../components/Flash/redux';
|
||||
import { FlashMessages } from '../../../components/Flash/redux/flash-messages';
|
||||
|
||||
import {
|
||||
isSignedInSelector,
|
||||
userTokenSelector
|
||||
} from '../../../redux/selectors';
|
||||
import { updateUserToken } from '../../../redux/actions';
|
||||
import { CodeAllyButton } from '../../../components/growth-book/codeally-button';
|
||||
|
||||
import RdbLocalLogoutAlert from './rdb-local-logout-alert';
|
||||
import RdbOnaContinueAlert from './rdb-ona-continue-alert';
|
||||
import RdbOnaLogoutAlert from './rdb-ona-logout-alert';
|
||||
|
||||
const mapStateToProps = (state: unknown) => ({
|
||||
isSignedIn: isSignedInSelector(state),
|
||||
userToken: userTokenSelector(state) as string | null
|
||||
});
|
||||
|
||||
const mapDispatchToProps = {
|
||||
createFlashMessage,
|
||||
updateUserToken
|
||||
};
|
||||
|
||||
interface RdbOnaInstructionsProps {
|
||||
course: string;
|
||||
createFlashMessage: typeof createFlashMessage;
|
||||
interface OneInstructionsProps {
|
||||
challengeType: number;
|
||||
copyUrl: () => void;
|
||||
copyUserToken: () => void;
|
||||
generateUserToken: () => Promise<void>;
|
||||
isSignedIn: boolean;
|
||||
updateUserToken: (arg0: string) => void;
|
||||
url: string;
|
||||
title: string;
|
||||
userToken: string | null;
|
||||
}
|
||||
|
||||
function RdbOnaInstructions({
|
||||
course,
|
||||
createFlashMessage,
|
||||
export function OnaInstructions({
|
||||
challengeType,
|
||||
copyUrl,
|
||||
copyUserToken,
|
||||
generateUserToken,
|
||||
isSignedIn,
|
||||
updateUserToken,
|
||||
url,
|
||||
title,
|
||||
userToken
|
||||
}: RdbOnaInstructionsProps): JSX.Element {
|
||||
}: OneInstructionsProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const coderoadTutorial = `https://raw.githubusercontent.com/${url}/main/tutorial.json`;
|
||||
function openOna() {
|
||||
const repoUrl = `https://github.com/freeCodeCamp/rdb-alpha`;
|
||||
const onaDomain = `https://app.ona.com/`;
|
||||
const onaUrl = `${onaDomain}#${repoUrl}`;
|
||||
|
||||
const generateUserToken = async () => {
|
||||
const createUserTokenResponse = await postUserToken();
|
||||
const { data = { userToken: null } } = createUserTokenResponse;
|
||||
|
||||
if (data?.userToken) {
|
||||
updateUserToken(data.userToken);
|
||||
createFlashMessage({
|
||||
type: 'success',
|
||||
message: FlashMessages.UserTokenGenerated
|
||||
});
|
||||
} else {
|
||||
createFlashMessage({
|
||||
type: 'danger',
|
||||
message: FlashMessages.UserTokenGenerateError
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const copyUserToken = () => {
|
||||
navigator.clipboard.writeText(userToken ?? '').then(
|
||||
() => {
|
||||
createFlashMessage({
|
||||
type: 'success',
|
||||
message: FlashMessages.UserTokenCopied
|
||||
});
|
||||
},
|
||||
() => {
|
||||
createFlashMessage({
|
||||
type: 'danger',
|
||||
message: FlashMessages.UserTokenCopyError
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const copyUrl = () => {
|
||||
navigator.clipboard.writeText(coderoadTutorial ?? '').then(
|
||||
() => {
|
||||
createFlashMessage({
|
||||
type: 'success',
|
||||
message: FlashMessages.CourseUrlCopied
|
||||
});
|
||||
},
|
||||
() => {
|
||||
createFlashMessage({
|
||||
type: 'danger',
|
||||
message: FlashMessages.CourseUrlCopyError
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
window.open(onaUrl, '_blank');
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='ca-description'>
|
||||
@@ -158,8 +97,6 @@ function RdbOnaInstructions({
|
||||
<code>placeholder</code>
|
||||
</Trans>
|
||||
</li>
|
||||
<Spacer size='xs' />
|
||||
<RdbLocalLogoutAlert course={course} />
|
||||
</ol>
|
||||
<Spacer size='s' />
|
||||
</>
|
||||
@@ -185,6 +122,7 @@ function RdbOnaInstructions({
|
||||
<code>placeholder</code>
|
||||
</Trans>
|
||||
</li>
|
||||
<li>{t('learn.local.step-6')}</li>
|
||||
<li>{t('learn.local.step-7')}</li>
|
||||
<Spacer size='xxs' />
|
||||
<Button block={true} onClick={copyUrl}>
|
||||
@@ -196,10 +134,10 @@ function RdbOnaInstructions({
|
||||
</li>
|
||||
<li>{t('learn.ona.step-9')}</li>
|
||||
</ol>
|
||||
<Spacer size='m' />
|
||||
<RdbOnaContinueAlert course={title} />
|
||||
{isSignedIn && <RdbOnaLogoutAlert course={title} />}
|
||||
<CodeAllyButton challengeType={challengeType} onClick={openOna} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
RdbOnaInstructions.displayName = 'RdbOnaInstructions';
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(RdbOnaInstructions);
|
||||
@@ -3,17 +3,15 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Callout } from '@freecodecamp/ui';
|
||||
|
||||
interface RdbLocalLogoutAlertProps {
|
||||
course: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
function RdbLocalLogoutAlert({
|
||||
course
|
||||
}: RdbLocalLogoutAlertProps): JSX.Element {
|
||||
function RdbLocalLogoutAlert({ title }: RdbLocalLogoutAlertProps): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Callout variant='danger'>
|
||||
{t('learn.local.logout-warning', { course })}
|
||||
{t('learn.local.logout-warning', { course: title })}
|
||||
</Callout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Package Utilities
|
||||
import { graphql } from 'gatsby';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import React, { Fragment, useEffect, useRef } from 'react';
|
||||
import Helmet from 'react-helmet';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
@@ -48,16 +48,13 @@ import { FlashMessages } from '../../../components/Flash/redux/flash-messages';
|
||||
import { SuperBlocks } from '../../../../../shared-dist/config/curriculum';
|
||||
import { CodeAllyDown } from '../../../components/growth-book/codeally-down';
|
||||
import { postUserToken } from '../../../utils/ajax';
|
||||
import { CodeAllyButton } from '../../../components/growth-book/codeally-button';
|
||||
|
||||
import RdbOnaContinueAlert from './rdb-ona-continue-alert';
|
||||
import RdbOnaInstructions from './rdb-ona-instructions';
|
||||
import RdbOnaLogoutAlert from './rdb-ona-logout-alert';
|
||||
import RdbLocalInstructions from './rdb-local-instructions';
|
||||
import RdbStep1Instructions from './rdb-step-1-instructions';
|
||||
import RdbStep2Instructions from './rdb-step-2-instructions';
|
||||
import { LocalInstructions } from './local-instructions';
|
||||
import { OnaInstructions } from './ona-instructions';
|
||||
|
||||
import './codeally.css';
|
||||
import { CodespacesInstructions } from './codespaces-instructions';
|
||||
|
||||
// Redux
|
||||
const mapStateToProps = createSelector(
|
||||
@@ -116,33 +113,43 @@ interface ShowCodeAllyProps {
|
||||
userToken: string | null;
|
||||
}
|
||||
|
||||
function ShowCodeAlly(props: ShowCodeAllyProps) {
|
||||
function ShowCodeAlly({
|
||||
completedChallenges,
|
||||
data,
|
||||
isChallengeCompleted,
|
||||
isSignedIn,
|
||||
partiallyCompletedChallenges,
|
||||
t,
|
||||
updateSolutionFormValues,
|
||||
userToken,
|
||||
updateUserToken,
|
||||
createFlashMessage,
|
||||
challengeMounted,
|
||||
initTests,
|
||||
pageContext: { challengeMeta },
|
||||
updateChallengeMeta,
|
||||
openCompletionModal
|
||||
}: ShowCodeAllyProps) {
|
||||
const container = useRef<HTMLElement>(null);
|
||||
|
||||
const {
|
||||
completedChallenges,
|
||||
data: {
|
||||
challengeNode: {
|
||||
challenge: {
|
||||
block,
|
||||
challengeType,
|
||||
description,
|
||||
id: challengeId,
|
||||
instructions,
|
||||
notes,
|
||||
superBlock,
|
||||
title,
|
||||
translationPending,
|
||||
url
|
||||
}
|
||||
challengeNode: {
|
||||
challenge: {
|
||||
block,
|
||||
challengeType,
|
||||
fields: { tests },
|
||||
description,
|
||||
helpCategory,
|
||||
id: challengeId,
|
||||
instructions,
|
||||
notes,
|
||||
superBlock,
|
||||
title,
|
||||
translationPending,
|
||||
url
|
||||
}
|
||||
},
|
||||
isChallengeCompleted,
|
||||
isSignedIn,
|
||||
partiallyCompletedChallenges,
|
||||
t,
|
||||
updateSolutionFormValues
|
||||
} = props;
|
||||
}
|
||||
} = data;
|
||||
|
||||
const blockNameTitle = `${t(
|
||||
`intro:${superBlock}.blocks.${block}.title`
|
||||
@@ -158,22 +165,6 @@ function ShowCodeAlly(props: ShowCodeAllyProps) {
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const {
|
||||
challengeMounted,
|
||||
data: {
|
||||
challengeNode: {
|
||||
challenge: {
|
||||
fields: { tests },
|
||||
challengeType,
|
||||
helpCategory,
|
||||
title
|
||||
}
|
||||
}
|
||||
},
|
||||
pageContext: { challengeMeta },
|
||||
initTests,
|
||||
updateChallengeMeta
|
||||
} = props;
|
||||
initTests(tests);
|
||||
const challengePaths = getChallengePaths({
|
||||
currentCurriculumPaths: challengeMeta
|
||||
@@ -191,54 +182,11 @@ function ShowCodeAlly(props: ShowCodeAllyProps) {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
function openOna() {
|
||||
const repoUrl = `https://github.com/freeCodeCamp/rdb-alpha`;
|
||||
const onaDomain = `https://app.ona.com/`;
|
||||
const onaUrl = `${onaDomain}#${repoUrl}`;
|
||||
|
||||
window.open(onaUrl, '_blank');
|
||||
}
|
||||
|
||||
const startCourse = async () => {
|
||||
const { isSignedIn, userToken, updateUserToken } = props;
|
||||
|
||||
if (!isSignedIn) {
|
||||
openOna();
|
||||
} else if (!userToken) {
|
||||
const createUserTokenResponse = await postUserToken();
|
||||
const { data = { userToken: null } } = createUserTokenResponse;
|
||||
|
||||
if (data?.userToken) {
|
||||
updateUserToken(data.userToken);
|
||||
openOna();
|
||||
} else {
|
||||
createFlashMessage({
|
||||
type: 'danger',
|
||||
message: FlashMessages.StartProjectErr
|
||||
});
|
||||
}
|
||||
} else {
|
||||
openOna();
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = ({
|
||||
showCompletionModal
|
||||
}: {
|
||||
showCompletionModal: boolean;
|
||||
}) => {
|
||||
const {
|
||||
completedChallenges,
|
||||
createFlashMessage,
|
||||
data: {
|
||||
challengeNode: {
|
||||
challenge: { id: challengeId }
|
||||
}
|
||||
},
|
||||
openCompletionModal,
|
||||
partiallyCompletedChallenges
|
||||
} = props;
|
||||
|
||||
const isPartiallyCompleted = partiallyCompletedChallenges.some(
|
||||
challenge => challenge.id === challengeId
|
||||
);
|
||||
@@ -257,7 +205,85 @@ function ShowCodeAlly(props: ShowCodeAllyProps) {
|
||||
}
|
||||
};
|
||||
|
||||
const onaDeprecated = useFeature('gitpod-deprecated').on;
|
||||
const rdbLocalInstructions = useFeature('rdb-local-instructions');
|
||||
const rdbCodespacesInstructions = useFeature('rdb-codespaces-instructions');
|
||||
const rdbOnaInstructions = useFeature('rdb-ona-instructions');
|
||||
|
||||
const coderoadTutorial = `https://raw.githubusercontent.com/${url}/main/tutorial.json`;
|
||||
|
||||
async function generateUserToken() {
|
||||
const createUserTokenResponse = await postUserToken();
|
||||
const { data = { userToken: null } } = createUserTokenResponse;
|
||||
|
||||
if (data?.userToken) {
|
||||
updateUserToken(data.userToken);
|
||||
createFlashMessage({
|
||||
type: 'success',
|
||||
message: FlashMessages.UserTokenGenerated
|
||||
});
|
||||
} else {
|
||||
createFlashMessage({
|
||||
type: 'danger',
|
||||
message: FlashMessages.UserTokenGenerateError
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function copyUserToken() {
|
||||
navigator.clipboard.writeText(userToken ?? '').then(
|
||||
() => {
|
||||
createFlashMessage({
|
||||
type: 'success',
|
||||
message: FlashMessages.UserTokenCopied
|
||||
});
|
||||
},
|
||||
() => {
|
||||
createFlashMessage({
|
||||
type: 'danger',
|
||||
message: FlashMessages.UserTokenCopyError
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function copyUrl() {
|
||||
navigator.clipboard.writeText(coderoadTutorial ?? '').then(
|
||||
() => {
|
||||
createFlashMessage({
|
||||
type: 'success',
|
||||
message: FlashMessages.CourseUrlCopied
|
||||
});
|
||||
},
|
||||
() => {
|
||||
createFlashMessage({
|
||||
type: 'danger',
|
||||
message: FlashMessages.CourseUrlCopyError
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const setups = [
|
||||
{
|
||||
name: t('learn.codespaces.summary'),
|
||||
component: CodespacesInstructions,
|
||||
on: rdbCodespacesInstructions.on
|
||||
},
|
||||
{
|
||||
name: t('learn.local.summary'),
|
||||
component: LocalInstructions,
|
||||
on: rdbLocalInstructions.on
|
||||
},
|
||||
{
|
||||
name: t('learn.ona.summary'),
|
||||
component: OnaInstructions,
|
||||
on: rdbOnaInstructions.on
|
||||
}
|
||||
];
|
||||
|
||||
const setupsToShow = setups.filter(setup => {
|
||||
return setup.on;
|
||||
});
|
||||
|
||||
return (
|
||||
<Hotkeys containerRef={container}>
|
||||
@@ -279,88 +305,55 @@ function ShowCodeAlly(props: ShowCodeAllyProps) {
|
||||
<PrismFormatted text={description} />
|
||||
<Spacer size='m' />
|
||||
|
||||
{onaDeprecated ? (
|
||||
{setupsToShow.map(({ name, component: SetupComponent }, i) => (
|
||||
<Fragment key={name}>
|
||||
<details
|
||||
open={i === 0}
|
||||
style={{ border: '1px solid #ccc', padding: '16px' }}
|
||||
>
|
||||
<summary>{name}</summary>
|
||||
<Spacer size='s' />
|
||||
<SetupComponent
|
||||
{...{
|
||||
challengeType,
|
||||
copyUrl,
|
||||
copyUserToken,
|
||||
generateUserToken,
|
||||
isSignedIn,
|
||||
title,
|
||||
userToken
|
||||
}}
|
||||
/>
|
||||
</details>
|
||||
<Spacer size='s' />
|
||||
</Fragment>
|
||||
))}
|
||||
|
||||
<Spacer size='m' />
|
||||
{isSignedIn && challengeType === challengeTypes.codeAllyCert && (
|
||||
<>
|
||||
<RdbLocalInstructions course={title} url={url} />
|
||||
<div className='ca-description'>
|
||||
{t('learn.complete-both-steps')}
|
||||
</div>
|
||||
<hr />
|
||||
<Spacer size='m' />
|
||||
{isSignedIn &&
|
||||
challengeType === challengeTypes.codeAllyCert && (
|
||||
<>
|
||||
<div className='ca-description'>
|
||||
{t('learn.complete-both-steps')}
|
||||
</div>
|
||||
<hr />
|
||||
<Spacer size='m' />
|
||||
<RdbStep1Instructions
|
||||
instructions={instructions}
|
||||
isCompleted={isPartiallyCompleted || isCompleted}
|
||||
/>
|
||||
<hr />
|
||||
<Spacer size='m' />
|
||||
<RdbStep2Instructions
|
||||
isCompleted={isCompleted}
|
||||
notes={notes}
|
||||
/>
|
||||
<Spacer size='m' />
|
||||
<SolutionForm
|
||||
challengeType={challengeType}
|
||||
description={description}
|
||||
onSubmit={handleSubmit}
|
||||
updateSolutionForm={updateSolutionFormValues}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<RdbOnaInstructions course={title} url={url} />
|
||||
<RdbStep1Instructions
|
||||
instructions={instructions}
|
||||
isCompleted={isPartiallyCompleted || isCompleted}
|
||||
/>
|
||||
<hr />
|
||||
<Spacer size='m' />
|
||||
{isSignedIn &&
|
||||
challengeType === challengeTypes.codeAllyCert ? (
|
||||
<>
|
||||
<div className='ca-description'>
|
||||
{t('learn.complete-both-steps')}
|
||||
</div>
|
||||
<hr />
|
||||
<Spacer size='m' />
|
||||
<RdbStep1Instructions
|
||||
instructions={instructions}
|
||||
isCompleted={isPartiallyCompleted || isCompleted}
|
||||
/>
|
||||
<Spacer size='m' />
|
||||
<RdbOnaContinueAlert course={title} />
|
||||
{isSignedIn && <RdbOnaLogoutAlert course={title} />}
|
||||
<CodeAllyButton
|
||||
challengeType={challengeType}
|
||||
//eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
onClick={startCourse}
|
||||
/>
|
||||
<hr />
|
||||
<Spacer size='m' />
|
||||
<RdbStep2Instructions
|
||||
isCompleted={isCompleted}
|
||||
notes={notes}
|
||||
/>
|
||||
<Spacer size='m' />
|
||||
<SolutionForm
|
||||
challengeType={challengeType}
|
||||
description={description}
|
||||
onSubmit={handleSubmit}
|
||||
updateSolutionForm={updateSolutionFormValues}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<RdbOnaContinueAlert course={title} />
|
||||
{isSignedIn && <RdbOnaLogoutAlert course={title} />}
|
||||
<CodeAllyButton
|
||||
challengeType={challengeType}
|
||||
//eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
onClick={startCourse}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<Spacer size='xxs' />
|
||||
<RdbStep2Instructions
|
||||
isCompleted={isCompleted}
|
||||
notes={notes}
|
||||
/>
|
||||
<Spacer size='m' />
|
||||
<SolutionForm
|
||||
challengeType={challengeType}
|
||||
description={description}
|
||||
onSubmit={handleSubmit}
|
||||
updateSolutionForm={updateSolutionFormValues}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ test.describe('After creating token', () => {
|
||||
await page.goto(
|
||||
'/learn/relational-database/learn-bash-by-building-a-boilerplate/build-a-boilerplate'
|
||||
);
|
||||
await page.getByRole('button', { name: 'Start the course' }).click();
|
||||
await page.getByText('Generate User Token').first().click();
|
||||
|
||||
await page.goto('/settings');
|
||||
// Set `exact` to `true` to only match the panel heading
|
||||
|
||||
Reference in New Issue
Block a user