Compare commits

...

5 Commits

Author SHA1 Message Date
Ihor Romaniuk
d9cce51d86 fix: date format depends on locale date format (#173) 2023-11-16 07:23:14 -03:00
vladislavkeblysh
925a7392cb feat: fixed lint 2023-11-14 12:37:33 -03:00
vladislavkeblysh
42bea23bd1 feat: fixed layout 2023-11-14 12:37:33 -03:00
Stanislav Lunyachek
833de88e1c fix: Missed favicon in Safari 2023-11-14 12:36:40 -03:00
Omar Al-Ithawi
bd85312ab3 feat: use atlas in make pull_translations on palm (#156)
Changes
-------
 - Bump frontend-platform to bring intl-imports.js script
 - Move all i18n imports into `src/i18n/index.js` so intl-imports.js can
   override it with latest translations
 - Add `atlas` into `make pull_translations` when `OPENEDX_ATLAS_PULL`
   environment variable is set.
 - Fixed lint rules for frontend-platform@4.1.0
 - Mock useTrackColorSchemeChoice to avoid test failures
 - Remove all broken and deprecated Tranisfex use
 - Install openedx-atlas

Refs: [FC-0012 project](https://openedx.atlassian.net/l/cp/XGS0iCcQ) implementing Translation Infrastructure OEP-58.
2023-11-03 16:57:13 -04:00
11 changed files with 73 additions and 57 deletions

View File

@@ -1,8 +0,0 @@
[main]
host = https://www.transifex.com
[o:open-edx:p:edx-platform:r:frontend-app-communications]
file_filter = src/i18n/messages/<lang>.json
source_file = src/i18n/transifex_input.json
source_lang = en
type = KEYVALUEJSON

View File

@@ -1,6 +1,4 @@
export TRANSIFEX_RESOURCE = frontend-app-communications
transifex_langs = "ar,fr,es_419,zh_CN"
intl_imports = ./node_modules/.bin/intl-imports.js
transifex_utils = ./node_modules/.bin/transifex-utils.js
i18n = ./src/i18n
transifex_input = $(i18n)/transifex_input.json
@@ -31,20 +29,17 @@ detect_changed_source_translations:
# Checking for changed translations...
git diff --exit-code $(i18n)
# Pushes translations to Transifex. You must run make extract_translations first.
push_translations:
# Pushing strings to Transifex...
tx push -s
# Fetching hashes from Transifex...
./node_modules/@edx/reactifex/bash_scripts/get_hashed_strings_v3.sh
# Writing out comments to file...
$(transifex_utils) $(transifex_temp) --comments --v3-scripts-path
# Pushing comments to Transifex...
./node_modules/@edx/reactifex/bash_scripts/put_comments_v3.sh
# Pulls translations from Transifex.
pull_translations:
tx pull -t -f --mode reviewed --languages=$(transifex_langs)
rm -rf src/i18n/messages
mkdir src/i18n/messages
cd src/i18n/messages \
&& atlas pull \
translations/frontend-component-header/src/i18n/messages:frontend-component-header \
translations/frontend-component-footer/src/i18n/messages:frontend-component-footer \
translations/paragon/src/i18n/messages:paragon \
translations/frontend-app-communications/src/i18n/messages:frontend-app-communications
$(intl_imports) frontend-component-header frontend-component-footer paragon frontend-app-communications
# This target is used by Travis.
validate-no-uncommitted-package-lock-changes:

11
package-lock.json generated
View File

@@ -12,7 +12,8 @@
"@edx/brand": "npm:@edx/brand-openedx@1.1.0",
"@edx/frontend-component-footer": "^12.0.0",
"@edx/frontend-component-header": "^4.0.0",
"@edx/frontend-platform": "^4.0.1",
"@edx/frontend-platform": "^4.2.0",
"@edx/openedx-atlas": "^0.5.0",
"@edx/paragon": "^20.20.0",
"@edx/tinymce-language-selector": "1.1.0",
"@fortawesome/fontawesome-svg-core": "1.2.36",
@@ -3472,6 +3473,14 @@
"@newrelic/publish-sourcemap": "^5.0.1"
}
},
"node_modules/@edx/openedx-atlas": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@edx/openedx-atlas/-/openedx-atlas-0.5.0.tgz",
"integrity": "sha512-wgcquAVj2BVw1rS7m1LZzUg6wVku37gs/Ks14LPWefpEIQ0HcTFV4nkg8dWfGX5dP3GiEp8s1Up7XwXzlErQhQ==",
"bin": {
"atlas": "atlas"
}
},
"node_modules/@edx/paragon": {
"version": "20.34.0",
"resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-20.34.0.tgz",

View File

@@ -36,7 +36,8 @@
"@edx/brand": "npm:@edx/brand-openedx@1.1.0",
"@edx/frontend-component-footer": "^12.0.0",
"@edx/frontend-component-header": "^4.0.0",
"@edx/frontend-platform": "^4.0.1",
"@edx/frontend-platform": "^4.2.0",
"@edx/openedx-atlas": "^0.5.0",
"@edx/paragon": "^20.20.0",
"@edx/tinymce-language-selector": "1.1.0",
"@fortawesome/fontawesome-svg-core": "1.2.36",

View File

@@ -4,6 +4,7 @@
<title>Communications | <%= process.env.SITE_NAME %></title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="<%=htmlWebpackPlugin.options.FAVICON_URL%>" type="image/x-icon" />
</head>
<body>
<div id="root"></div>

View File

@@ -9,6 +9,8 @@ function ScheduleEmailForm(props) {
const isMobile = useMobileResponsive();
const { isValid, onDateTimeChange, dateTime } = props;
const { date, time } = dateTime;
const descriptionDate = new Date();
descriptionDate.setDate(new Date().getDate() + 1);
return (
<Form.Group>
<div className={classNames('d-flex', isMobile ? 'flex-column' : 'flex-row', 'my-3')}>
@@ -30,7 +32,10 @@ function ScheduleEmailForm(props) {
<small className="text-gray-500 x-small">
<FormattedMessage
id="bulk.email.form.schedule.date.description"
defaultMessage="Enter a start date, e.g. 11/27/2023"
defaultMessage="Enter a start date, e.g. {date}"
values={{
date: descriptionDate.toLocaleDateString(),
}}
/>
</small>
</div>
@@ -52,7 +57,10 @@ function ScheduleEmailForm(props) {
<small className="text-gray-500 x-small">
<FormattedMessage
id="bulk.email.form.schedule.time.description"
defaultMessage="Enter a start time, e.g. 09:00 AM"
defaultMessage="Enter a start time, e.g. {time}"
values={{
time: descriptionDate.toLocaleTimeString([], { timeStyle: 'short' }),
}}
/>
</small>
</div>

View File

@@ -26,6 +26,7 @@ function flattenScheduledEmailsArray(emails) {
emailId: email.courseEmail.id,
task: email.task,
taskDue: new Date(email.taskDue).toLocaleString(),
taskDueUTC: email.taskDue,
...email.courseEmail,
targets: email.courseEmail.targets.join(', '),
}));
@@ -91,10 +92,10 @@ function BulkEmailScheduledEmailsTable({ intl }) {
const handleEditEmail = (row) => {
const {
original: {
htmlMessage: emailBody, subject: emailSubject, taskDue, targets, schedulingId, emailId,
htmlMessage: emailBody, subject: emailSubject, taskDueUTC, targets, schedulingId, emailId,
},
} = row;
const dateTime = new Date(taskDue);
const dateTime = new Date(taskDueUTC);
const emailRecipients = targets.replaceAll('-', ':').split(', ');
const scheduleDate = formatDate(dateTime);
const scheduleTime = formatTime(dateTime);

View File

@@ -59,18 +59,18 @@ export default function PageContainer(props) {
if (courseMetadata) {
return (
<CourseMetadataContext.Provider value={courseMetadata}>
<>
<Header
className="learning-header"
courseOrg={courseMetadata.org}
courseNumber={courseMetadata.number}
courseTitle={courseMetadata.title}
/>
<Header
className="learning-header"
courseOrg={courseMetadata.org}
courseNumber={courseMetadata.number}
courseTitle={courseMetadata.title}
/>
<div className="pb-3 container">
<main>
{children}
</main>
<Footer />
</>
</div>
<Footer />
</CourseMetadataContext.Provider>
);
}

View File

@@ -1,3 +1,7 @@
import { messages as footerMessages } from '@edx/frontend-component-footer';
import { messages as headerMessages } from '@edx/frontend-component-header';
import { messages as paragonMessages } from '@edx/paragon';
import arMessages from './messages/ar.json';
import caMessages from './messages/ca.json';
// no need to import en messages-- they are in the defaultMessage field
@@ -13,7 +17,7 @@ import ruMessages from './messages/ru.json';
import thMessages from './messages/th.json';
import ukMessages from './messages/uk.json';
const messages = {
const appMessages = {
ar: arMessages,
'es-419': es419Messages,
fr: frMessages,
@@ -29,4 +33,9 @@ const messages = {
uk: ukMessages,
};
export default messages;
export default [
headerMessages,
footerMessages,
paragonMessages,
appMessages,
];

View File

@@ -7,12 +7,9 @@ import {
import { AppProvider, AuthenticatedPageRoute, ErrorPage } from '@edx/frontend-platform/react';
import ReactDOM from 'react-dom';
import { messages as headerMessages } from '@edx/frontend-component-header';
import { messages as footerMessages } from '@edx/frontend-component-footer';
import { messages as paragonMessages } from '@edx/paragon';
import { Helmet } from 'react-helmet';
import { Switch } from 'react-router-dom';
import appMessages from './i18n';
import messages from './i18n';
import './index.scss';
import BulkEmailTool from './components/bulk-email-tool';
@@ -24,15 +21,13 @@ subscribe(APP_READY, () => {
<Helmet>
<link rel="shortcut icon" href={getConfig().FAVICON_URL} type="image/x-icon" />
</Helmet>
<div className="pb-3 container">
<Switch>
<AuthenticatedPageRoute path="/courses/:courseId/bulk_email">
<PageContainer>
<BulkEmailTool />
</PageContainer>
</AuthenticatedPageRoute>
</Switch>
</div>
<Switch>
<AuthenticatedPageRoute path="/courses/:courseId/bulk_email">
<PageContainer>
<BulkEmailTool />
</PageContainer>
</AuthenticatedPageRoute>
</Switch>
</AppProvider>,
document.getElementById('root'),
);
@@ -54,5 +49,5 @@ initialize({
);
},
},
messages: [appMessages, headerMessages, footerMessages, paragonMessages],
messages,
});

View File

@@ -8,7 +8,7 @@ import { configure as configureI18n, IntlProvider } from '@edx/frontend-platform
import { configure as configureLogging, MockLoggingService } from '@edx/frontend-platform/logging';
import { getConfig, mergeConfig } from '@edx/frontend-platform';
import { configure as configureAuth, MockAuthService } from '@edx/frontend-platform/auth';
import appMessages from './i18n';
import messages from './i18n';
jest.mock('@edx/frontend-platform/react/hooks', () => ({
...jest.requireActual('@edx/frontend-platform/react/hooks'),
@@ -47,13 +47,18 @@ export function initializeMockApp() {
const i18nService = configureI18n({
config: getConfig(),
loggingService,
messages: [appMessages],
messages,
});
const authService = configureAuth(MockAuthService, { config: getConfig(), loggingService });
return { loggingService, i18nService, authService };
}
jest.mock('@edx/frontend-platform/react/hooks', () => ({
...jest.requireActual('@edx/frontend-platform/react/hooks'),
useTrackColorSchemeChoice: jest.fn(),
}));
function render(ui, options) {
// eslint-disable-next-line react/prop-types
function Wrapper({ children }) {