feat: live app configuration's mockups (#261)
* feat: implement new mockups for live card
This commit is contained in:
@@ -36,10 +36,10 @@ function FieldFeedback({
|
||||
}
|
||||
|
||||
FieldFeedback.propTypes = {
|
||||
errorMessage: PropTypes.string.isRequired,
|
||||
feedbackMessage: PropTypes.string.isRequired,
|
||||
errorCondition: PropTypes.bool.isRequired,
|
||||
feedbackCondition: PropTypes.bool.isRequired,
|
||||
errorMessage: PropTypes.string,
|
||||
feedbackMessage: PropTypes.string,
|
||||
feedbackCondition: PropTypes.bool,
|
||||
feedbackClasses: PropTypes.string,
|
||||
transitionClasses: PropTypes.string,
|
||||
};
|
||||
@@ -47,6 +47,9 @@ FieldFeedback.propTypes = {
|
||||
FieldFeedback.defaultProps = {
|
||||
feedbackClasses: '',
|
||||
transitionClasses: '',
|
||||
feedbackMessage: '',
|
||||
feedbackCondition: false,
|
||||
errorMessage: '',
|
||||
};
|
||||
|
||||
export default FieldFeedback;
|
||||
|
||||
@@ -39,13 +39,19 @@ function FormikControl({
|
||||
|
||||
FormikControl.propTypes = {
|
||||
name: PropTypes.element.isRequired,
|
||||
label: PropTypes.element.isRequired,
|
||||
help: PropTypes.element.isRequired,
|
||||
className: PropTypes.string.isRequired,
|
||||
label: PropTypes.element,
|
||||
help: PropTypes.element,
|
||||
className: PropTypes.string,
|
||||
value: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.number,
|
||||
]).isRequired,
|
||||
};
|
||||
|
||||
FormikControl.defaultProps = {
|
||||
help: <></>,
|
||||
label: <></>,
|
||||
className: '',
|
||||
};
|
||||
|
||||
export default FormikControl;
|
||||
|
||||
@@ -21,6 +21,7 @@ const COURSE_APPS_ORDER = [
|
||||
'wiki',
|
||||
'calculator',
|
||||
'proctoring',
|
||||
'live',
|
||||
'textbooks',
|
||||
'custom_pages',
|
||||
];
|
||||
|
||||
@@ -153,7 +153,11 @@ function LtiConfigForm({ onSubmit, intl, formRef }) {
|
||||
</div>
|
||||
)}
|
||||
</Form>
|
||||
<AppExternalLinks externalLinks={externalLinks} providerName={providerName} />
|
||||
<AppExternalLinks
|
||||
externalLinks={externalLinks}
|
||||
providerName={providerName}
|
||||
customClasses="small text-muted"
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ function AppExternalLinks({
|
||||
externalLinks,
|
||||
intl,
|
||||
providerName,
|
||||
showLaunchIcon,
|
||||
customClasses,
|
||||
}) {
|
||||
const { contactEmail, ...links } = externalLinks;
|
||||
const linkTypes = Object.keys(links).filter(key => links[key]);
|
||||
@@ -24,12 +26,13 @@ function AppExternalLinks({
|
||||
<AppConfigFormDivider />
|
||||
<h4 className="pt-4">{intl.formatMessage(messages.linkTextHeading)}</h4>
|
||||
{linkTypes.map((type) => (
|
||||
<div key={type} className="small text-muted">
|
||||
<div key={type}>
|
||||
<Hyperlink
|
||||
destination={externalLinks[type]}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
showLaunchIcon={false}
|
||||
showLaunchIcon={showLaunchIcon}
|
||||
className={customClasses}
|
||||
>
|
||||
{ intl.formatMessage(messages[type], { providerName }) }
|
||||
</Hyperlink>
|
||||
@@ -38,7 +41,7 @@ function AppExternalLinks({
|
||||
</>
|
||||
) : null}
|
||||
{contactEmail && (
|
||||
<div className="small text-muted">
|
||||
<div className={customClasses}>
|
||||
<hr />
|
||||
<FormattedMessage
|
||||
{...messages.contact}
|
||||
@@ -48,7 +51,7 @@ function AppExternalLinks({
|
||||
to={contactEmail}
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{ contactEmail }
|
||||
<span className={customClasses}>{ contactEmail }</span>
|
||||
</MailtoLink>
|
||||
),
|
||||
}}
|
||||
@@ -69,6 +72,13 @@ AppExternalLinks.propTypes = {
|
||||
}).isRequired,
|
||||
providerName: PropTypes.string.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
showLaunchIcon: PropTypes.bool,
|
||||
customClasses: PropTypes.string,
|
||||
};
|
||||
|
||||
AppExternalLinks.defaultProps = {
|
||||
showLaunchIcon: false,
|
||||
customClasses: '',
|
||||
};
|
||||
|
||||
export default injectIntl(AppExternalLinks);
|
||||
|
||||
@@ -7,9 +7,10 @@ export const filterItemFromObject = (array, key, value) => (
|
||||
array.filter(item => item[key] !== value)
|
||||
);
|
||||
|
||||
export const checkFieldErrors = (touched, errors, fieldPath, propertyName) => Boolean(
|
||||
getIn(errors, `${fieldPath}.${propertyName}`) && getIn(touched, `${fieldPath}.${propertyName}`),
|
||||
);
|
||||
export const checkFieldErrors = (touched, errors, fieldPath, propertyName) => {
|
||||
const path = fieldPath ? `${fieldPath}.${propertyName}` : propertyName;
|
||||
return Boolean(getIn(errors, path) && getIn(touched, path));
|
||||
};
|
||||
|
||||
export const errorExists = (errors, fieldPath, propertyName) => getIn(errors, `${fieldPath}.${propertyName}`);
|
||||
|
||||
|
||||
128
src/pages-and-resources/live/Settings.jsx
Normal file
128
src/pages-and-resources/live/Settings.jsx
Normal file
@@ -0,0 +1,128 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import * as Yup from 'yup';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { SelectableBox, Icon } from '@edx/paragon';
|
||||
import { camelCase } from 'lodash';
|
||||
import FormikControl from '../../generic/FormikControl';
|
||||
import { useAppSetting } from '../../utils';
|
||||
import AppExternalLinks from '../discussions/app-config-form/apps/shared/AppExternalLinks';
|
||||
import AppSettingsModal from '../app-settings-modal/AppSettingsModal';
|
||||
import iconsSrc from './constants';
|
||||
import messages from './messages';
|
||||
|
||||
function LiveSettings({
|
||||
intl,
|
||||
onClose,
|
||||
}) {
|
||||
const [liveConfiguration, saveSettings] = useAppSetting('liveConfiguration');
|
||||
const liveData = {
|
||||
consumerKey: liveConfiguration?.consumerKey || '',
|
||||
consumerSecret: liveConfiguration?.consumerSecret || '',
|
||||
launchUrl: liveConfiguration?.launchUrl || '',
|
||||
launchEmail: liveConfiguration?.launchEmail || '',
|
||||
provider: liveConfiguration?.provider || 'zoom',
|
||||
piiSharingEnable: liveConfiguration?.piiSharing
|
||||
? liveConfiguration.piiShareUsername && liveConfiguration.piiShareEmail
|
||||
: false,
|
||||
};
|
||||
const validationSchema = {
|
||||
enabled: Yup.boolean(),
|
||||
consumerKey: Yup.string().required(intl.formatMessage(messages.consumerKeyRequired)),
|
||||
consumerSecret: Yup.string().required(intl.formatMessage(messages.consumerSecretRequired)),
|
||||
launchUrl: Yup.string().required(intl.formatMessage(messages.launchUrlRequired)),
|
||||
launchEmail: Yup.string().required(intl.formatMessage(messages.launchEmailRequired)),
|
||||
};
|
||||
|
||||
const handleSettingsSave = async (values) => saveSettings(values);
|
||||
const handleProviderChange = (selectedProvider, setFieldValue) => {
|
||||
setFieldValue('provider', selectedProvider);
|
||||
};
|
||||
|
||||
return (
|
||||
<AppSettingsModal
|
||||
appId="live"
|
||||
title={intl.formatMessage(messages.heading)}
|
||||
enableAppHelp={intl.formatMessage(messages.enableLiveHelp)}
|
||||
enableAppLabel={intl.formatMessage(messages.enableLiveLabel)}
|
||||
learnMoreText={intl.formatMessage(messages.enableLiveLink)}
|
||||
onClose={onClose}
|
||||
initialValues={liveData}
|
||||
validationSchema={validationSchema}
|
||||
onSettingsSave={handleSettingsSave}
|
||||
configureBeforeEnable
|
||||
>
|
||||
{
|
||||
({ values, setFieldValue }) => (
|
||||
<>
|
||||
<h4 className="my-3">{intl.formatMessage(messages.selectProvider)}</h4>
|
||||
<SelectableBox.Set
|
||||
type="checkbox"
|
||||
value={values.provider}
|
||||
onChange={(event) => handleProviderChange(event.target.value, setFieldValue)}
|
||||
name="provider"
|
||||
columns={3}
|
||||
className="mb-3"
|
||||
>
|
||||
{[{ id: 'zoom' }, { id: 'google meet' }, { id: 'microsoft teams' }].map((app) => (
|
||||
<SelectableBox value={app.id} type="checkbox" key={app.id}>
|
||||
<div className="d-flex flex-column align-items-center">
|
||||
<Icon src={iconsSrc[`${camelCase(app.id)}`]} alt={app.id} />
|
||||
<span>{intl.formatMessage(messages[`appName-${camelCase(app.id)}`])}</span>
|
||||
</div>
|
||||
</SelectableBox>
|
||||
))}
|
||||
</SelectableBox.Set>
|
||||
<p>{intl.formatMessage(messages.providerHelperText, { providerName: 'Zoom' })}</p>
|
||||
{liveData.piiSharingEnable ? (
|
||||
<>
|
||||
<p className="pb-2">{intl.formatMessage(messages.formInstructions)}</p>
|
||||
<FormikControl
|
||||
name="consumerKey"
|
||||
value={values.consumerKey}
|
||||
floatingLabel={intl.formatMessage(messages.consumerKey)}
|
||||
className="pb-1"
|
||||
type="input"
|
||||
/>
|
||||
<FormikControl
|
||||
name="consumerSecret"
|
||||
value={values.consumerSecret}
|
||||
floatingLabel={intl.formatMessage(messages.consumerSecret)}
|
||||
className="pb-1"
|
||||
type="input"
|
||||
/>
|
||||
<FormikControl
|
||||
name="launchUrl"
|
||||
value={values.launchUrl}
|
||||
floatingLabel={intl.formatMessage(messages.launchUrl)}
|
||||
className="pb-1"
|
||||
type="input"
|
||||
/>
|
||||
<FormikControl
|
||||
name="launchEmail"
|
||||
value={values.launchEmail}
|
||||
floatingLabel={intl.formatMessage(messages.launchEmail)}
|
||||
type="input"
|
||||
/>
|
||||
<AppExternalLinks
|
||||
externalLinks={liveConfiguration?.documentationLinks}
|
||||
providerName="live"
|
||||
showLaunchIcon
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<p>{intl.formatMessage(messages.requestPiiSharingEnable)}</p>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
</AppSettingsModal>
|
||||
);
|
||||
}
|
||||
|
||||
LiveSettings.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default injectIntl(LiveSettings);
|
||||
9
src/pages-and-resources/live/constants.js
Normal file
9
src/pages-and-resources/live/constants.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import { GoogleMeet, MicrosoftTeams, Zoom } from '@edx/paragon/icons';
|
||||
|
||||
const iconsSrc = {
|
||||
googleMeet: GoogleMeet,
|
||||
microsoftTeams: MicrosoftTeams,
|
||||
zoom: Zoom,
|
||||
};
|
||||
|
||||
export { iconsSrc as default };
|
||||
146
src/pages-and-resources/live/messages.js
Normal file
146
src/pages-and-resources/live/messages.js
Normal file
@@ -0,0 +1,146 @@
|
||||
import { defineMessages } from '@edx/frontend-platform/i18n';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: {
|
||||
id: 'authoring.pagesAndResources.live.enableLive.heading',
|
||||
defaultMessage: 'Configure Live',
|
||||
description: 'Heading for live configuration',
|
||||
},
|
||||
enableLiveLabel: {
|
||||
id: 'authoring.pagesAndResources.live.enableLive.label',
|
||||
defaultMessage: 'Live',
|
||||
description: 'Title for configuration',
|
||||
},
|
||||
enableLiveHelp: {
|
||||
id: 'authoring.pagesAndResources.live.enableLive.help',
|
||||
defaultMessage: 'Schedule meetings and conduct live course sessions with learners.',
|
||||
description: 'Tells the purpose of live configuration',
|
||||
},
|
||||
enableLiveLink: {
|
||||
id: 'authoring.pagesAndResources.live.enableLive.link',
|
||||
defaultMessage: 'Learn more about live',
|
||||
description: 'Link text that tells the user to learn about the live',
|
||||
},
|
||||
saveButton: {
|
||||
id: 'authoring.discussions.saveButton',
|
||||
defaultMessage: 'Save',
|
||||
description: 'Button allowing the user to submit their discussion configuration.',
|
||||
},
|
||||
savingButton: {
|
||||
id: 'authoring.discussions.savingButton',
|
||||
defaultMessage: 'Saving',
|
||||
description: 'Button label when the discussion configuration is being submitted.',
|
||||
},
|
||||
savedButton: {
|
||||
id: 'authoring.discussions.savedButton',
|
||||
defaultMessage: 'Saved',
|
||||
description: 'Button label when the discussion configuration has been successfully submitted.',
|
||||
},
|
||||
selectProvider: {
|
||||
id: 'authoring.live.selectProvider',
|
||||
defaultMessage: 'Select a video conferencing tool',
|
||||
description: '',
|
||||
},
|
||||
formInstructions: {
|
||||
id: 'authoring.live.formInstructions',
|
||||
defaultMessage: 'Complete the fields below to set up your video conferencing tool.',
|
||||
description: 'Instruction for configure the video conferencing tool.',
|
||||
},
|
||||
consumerKey: {
|
||||
id: 'authoring.live.consumerKey',
|
||||
defaultMessage: 'Consumer Key',
|
||||
description: 'Label for the Consumer Key field.',
|
||||
},
|
||||
consumerKeyRequired: {
|
||||
id: 'authoring.live.consumerKey.required',
|
||||
defaultMessage: 'Consumer key is a required field',
|
||||
description: 'Tells the user that the Consumer Key field is required and must have a value.',
|
||||
},
|
||||
consumerSecret: {
|
||||
id: 'authoring.live.consumerSecret',
|
||||
defaultMessage: 'Consumer Secret',
|
||||
description: 'Label for the Consumer Secret field.',
|
||||
},
|
||||
consumerSecretRequired: {
|
||||
id: 'authoring.live.consumerSecret.required',
|
||||
defaultMessage: 'Consumer secret is a required field',
|
||||
description: 'Tells the user that the Consumer Secret field is required and must have a value.',
|
||||
},
|
||||
launchUrl: {
|
||||
id: 'authoring.live.launchUrl',
|
||||
defaultMessage: 'Launch URL',
|
||||
description: 'Label for the Launch URL field.',
|
||||
},
|
||||
launchUrlRequired: {
|
||||
id: 'authoring.live.launchUrl.required',
|
||||
defaultMessage: 'Launch URL is a required field',
|
||||
description: 'Tells the user that the Launch URL field is required and must have a value.',
|
||||
},
|
||||
launchEmail: {
|
||||
id: 'authoring.live.launchEmail',
|
||||
defaultMessage: 'Launch Email',
|
||||
description: 'Label for the Launch Email field.',
|
||||
},
|
||||
launchEmailRequired: {
|
||||
id: 'authoring.live.launchEmail.required',
|
||||
defaultMessage: 'Launch Email is a required field',
|
||||
description: 'Tells the user that the Launch Email field is required and must have a value.',
|
||||
},
|
||||
providerHelperText: {
|
||||
id: 'authoring.live.provider.helpText',
|
||||
defaultMessage: 'This configuration will require sharing username and emails of learners and the course team with {providerName}',
|
||||
description: 'Tells the user that sharing username and email is required for configuration',
|
||||
},
|
||||
requestPiiSharingEnable: {
|
||||
id: 'authoring.live.requestPiiSharingEnable',
|
||||
defaultMessage: 'Request your edX support team to enable the PII sharing for this course, in order to access the LTI configurations for a provider',
|
||||
description: 'Tells the user that request edx support team to enable the PII sharing to access the LTO configuration for a provider.',
|
||||
},
|
||||
general: {
|
||||
id: 'authoring.live.appDocInstructions.documentationLink',
|
||||
defaultMessage: 'General documentation',
|
||||
description: 'Application Document Instructions message for documentation link',
|
||||
},
|
||||
accessibility: {
|
||||
id: 'authoring.live.appDocInstructions.accessibilityDocumentationLink',
|
||||
defaultMessage: 'Accessibility documentation',
|
||||
description: 'Application Document Instructions message for accessibility link',
|
||||
},
|
||||
configuration: {
|
||||
id: 'authoring.live.appDocInstructions.configurationLink',
|
||||
defaultMessage: 'Configuration documentation',
|
||||
description: 'Application Document Instructions message for configurations link',
|
||||
},
|
||||
learnMore: {
|
||||
id: 'authoring.live.appDocInstructions.learnMoreLink',
|
||||
defaultMessage: 'Learn more about {providerName}',
|
||||
description: 'Application Document Instructions message for learn more links',
|
||||
},
|
||||
linkTextHeading: {
|
||||
id: 'authoring.live.appDocInstructions.linkTextHeading',
|
||||
defaultMessage: 'External help and documentation',
|
||||
description: 'External help and documentation heading',
|
||||
},
|
||||
linkText: {
|
||||
id: 'authoring.live.appDocInstructions.linkText',
|
||||
defaultMessage: '{link}',
|
||||
description: 'link',
|
||||
},
|
||||
'appName-zoom': {
|
||||
id: 'authoring.live.appName-yellowdig',
|
||||
defaultMessage: 'Zoom',
|
||||
description: 'The name of the Zoom app.',
|
||||
},
|
||||
'appName-googleMeet': {
|
||||
id: 'authoring.live.appName-googleMeet',
|
||||
defaultMessage: 'Google Meet',
|
||||
description: 'The name of the Google Meet app.',
|
||||
},
|
||||
'appName-microsoftTeams': {
|
||||
id: 'authoring.live.appName-microsoftTeams',
|
||||
defaultMessage: 'Microsoft Teams',
|
||||
description: 'The name of the Microsoft Teams app.',
|
||||
},
|
||||
});
|
||||
|
||||
export default messages;
|
||||
Reference in New Issue
Block a user