[IMPROVE] Administration -> Mailer Rewrite to React. (#17191)

pull/17322/head
gabriellsh 6 years ago committed by GitHub
parent b57d5ed5d8
commit 03cddfd2da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      app/mail-messages/client/router.js
  2. 2
      app/mail-messages/client/views/index.js
  3. 62
      app/mail-messages/client/views/mailer.html
  4. 47
      app/mail-messages/client/views/mailer.js
  5. 99
      app/ui-admin/client/components/mailer/Mailer.js
  6. 11
      app/ui-admin/client/components/mailer/Mailer.stories.js
  7. 37
      app/ui-admin/client/components/mailer/MailerRoute.js
  8. 5
      app/ui-admin/client/routes.js
  9. 7
      app/utils/lib/isJSON.js
  10. 1
      packages/rocketchat-i18n/i18n/en.i18n.json

@ -2,18 +2,6 @@ import { Meteor } from 'meteor/meteor';
import { FlowRouter } from 'meteor/kadira:flow-router';
import { BlazeLayout } from 'meteor/kadira:blaze-layout';
import { registerAdminRoute } from '../../ui-admin/client';
registerAdminRoute('/mailer', {
name: 'admin-mailer',
async action() {
await import('./views');
return BlazeLayout.render('main', {
center: 'mailer',
});
},
});
FlowRouter.route('/mailer/unsubscribe/:_id/:createdAt', {
name: 'mailer-unsubscribe',
async action(params) {

@ -1,4 +1,2 @@
import './mailer.html';
import './mailer';
import './mailerUnsubscribe.html';
import './mailerUnsubscribe';

@ -1,62 +0,0 @@
<template name="mailer">
<section class="page-container page-list">
{{> header sectionName="Mailer"}}
<div class="content">
{{#unless hasPermission 'access-mailer'}}
<p>{{_ "You_are_not_authorized_to_view_this_page"}}</p>
{{else}}
<form>
<div class="rocket-form">
<fieldset>
<div class="input-line">
<label>{{_ "Email_from"}}</label>
<div>
<input type="text" class="rc-input__element" name="from" value="" placeholder="{{fromEmail}}" />
</div>
<div>
<small class="settings-description secondary-font-color">{{{_ "From_email_warning"}}}</small>
</div>
</div>
<div class="input-line">
<label>{{_ "Dry_run"}}</label>
<div>
<input type="checkbox" name="dryrun" value="1" />
</div>
<div>
<small class="settings-description secondary-font-color">{{{_ "Dry_run_description"}}}</small>
</div>
</div>
<div class="input-line">
<label>{{_ "Query"}}</label>
<div>
<input type="text" class="rc-input__element" name="query" value="" />
</div>
<div>
<small class="settings-description secondary-font-color">{{{_ "Query_description"}}}</small>
</div>
</div>
<div class="input-line">
<label>{{_ "Email_subject"}}</label>
<div>
<input type="text" class="rc-input__element" name="subject" value="" />
</div>
</div>
<div class="input-line">
<label>{{_ "Email_body"}}</label>
<div>
<textarea class="rc-input__element" name="body" rows="10" style="height: auto"></textarea>
</div>
<div>
<small class="settings-description secondary-font-color">{{{_ "Mailer_body_tags"}}}</small>
</div>
</div>
</fieldset>
<div class="submit">
<button class="button primary send"><i class="icon-send"></i><span>{{_ "Send_email"}}</span></button>
</div>
</div>
</form>
{{/unless}}
</div>
</section>
</template>

@ -1,47 +0,0 @@
import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import { Tracker } from 'meteor/tracker';
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
import toastr from 'toastr';
import { settings } from '../../../settings';
import { handleError } from '../../../utils';
import { SideNav } from '../../../ui-utils/client';
Template.mailer.helpers({
fromEmail() {
return settings.get('From_Email');
},
});
Template.mailer.events({
'click .send'(e, t) {
e.preventDefault();
const from = $(t.find('[name=from]')).val();
const subject = $(t.find('[name=subject]')).val();
const body = $(t.find('[name=body]')).val();
const dryrun = $(t.find('[name=dryrun]:checked')).val();
const query = $(t.find('[name=query]')).val();
if (!from) {
toastr.error(TAPi18n.__('error-invalid-from-address'));
return;
}
if (body.indexOf('[unsubscribe]') === -1) {
toastr.error(TAPi18n.__('error-missing-unsubscribe-link'));
return;
}
return Meteor.call('Mailer.sendMail', from, subject, body, dryrun, query, function(err) {
if (err) {
return handleError(err);
}
return toastr.success(TAPi18n.__('The_emails_are_being_sent'));
});
},
});
Template.mailer.onRendered(() => {
Tracker.afterFlush(() => {
SideNav.setFlex('adminFlex');
SideNav.openFlex();
});
});

@ -0,0 +1,99 @@
import React, { useState } from 'react';
import { TextInput, TextAreaInput, Field, FieldGroup, CheckBox, Button, Icon, ButtonGroup } from '@rocket.chat/fuselage';
import { useTranslation } from '../../../../../client/contexts/TranslationContext';
import { Page } from '../../../../../client/components/basic/Page';
import { isEmail } from '../../../../utils/lib/isEmail.js';
import { isJSON } from '../../../../utils/lib/isJSON.js';
export function Mailer({ sendMail = () => {}, ...props }) {
const t = useTranslation();
const [fromEmail, setFromEmail] = useState({ value: '', error: false });
const [dryRun, setDryRun] = useState(false);
const [query, setQuery] = useState({ value: '', error: false });
const [subject, setSubject] = useState('');
const [emailBody, setEmailBody] = useState('');
return <Page _id='mailer' {...props}>
<Page.Header title={t('Mailer')}></Page.Header>
<Page.ContentShadowScroll maxWidth='x600' alignSelf='center' display='flex' flexDirection='column'>
<FieldGroup is='form' method='post'>
<Field>
<Field.Label>{t('From')}</Field.Label>
<Field.Row>
<TextInput
id='fromEmail'
placeholder={t('Type_your_email')}
value={fromEmail.value}
error={fromEmail.error}
onChange={(e) => {
setFromEmail({
value: e.currentTarget.value,
error: !isEmail(e.currentTarget.value),
});
}}
/>
</Field.Row>
</Field>
<Field>
<Field.Row>
<CheckBox
id='dryRun'
checked={dryRun}
onChange={() => setDryRun(!dryRun)}
/>
<Field.Label htmlFor='dry-run'>
{t('Dry_run')}
</Field.Label>
</Field.Row>
<Field.Hint>{t('Dry_run_description')}</Field.Hint>
</Field>
<Field>
<Field.Label>{t('Query')}</Field.Label>
<Field.Row>
<TextInput id='query'
value={query.value}
error={query.error}
onChange={(e) => {
setQuery({
value: e.currentTarget.value,
error: e.currentTarget.value && !isJSON(e.currentTarget.value),
});
}}
/>
</Field.Row>
<Field.Hint>{t('Query_description')}</Field.Hint>
</Field>
<Field>
<Field.Label>{t('Subject')}</Field.Label>
<Field.Row>
<TextInput
id='subject'
value={subject.value}
error={subject.error}
onChange={(e) => {
setSubject(e.currentTarget.value);
}}
/>
</Field.Row>
</Field>
<Field>
<Field.Label>{t('Email_body')}</Field.Label>
<Field.Row>
<TextAreaInput
id='emailBody'
rows={10}
value={emailBody}
onChange={(e) => setEmailBody(e.currentTarget.value)}
/>
</Field.Row>
<Field.Hint dangerouslySetInnerHTML={{ __html: t('Mailer_body_tags') }}></Field.Hint>
</Field>
<ButtonGroup align='end'>
<Button primary onClick={() => { sendMail({ fromEmail, dryRun, query, subject, emailBody }); }}><Icon name='send' size='x20' mie='x8'/>{t('Send_email')}</Button>
</ButtonGroup>
</FieldGroup>
</Page.ContentShadowScroll>
</Page>;
}

@ -0,0 +1,11 @@
import React from 'react';
import { Mailer } from './Mailer';
export default {
title: 'admin/pages/mailer',
component: Mailer,
};
export const _default = () =>
<Mailer />;

@ -0,0 +1,37 @@
import React from 'react';
import toastr from 'toastr';
import { usePermission } from '../../../../../client/contexts/AuthorizationContext';
import { useMethod } from '../../../../../client/contexts/ServerContext';
import { useTranslation } from '../../../../../client/contexts/TranslationContext';
import { Mailer } from './Mailer';
import { NotAuthorizedPage } from '../settings/NotAuthorizedPage';
const useSendMail = () => {
const meteorSendMail = useMethod('Mailer.sendMail');
const t = useTranslation();
return ({ fromEmail, subject, emailBody, dryRun, query }) => {
if (query.error) {
toastr.error(t('Query_is_not_valid_JSON'));
return;
}
if (fromEmail.error || fromEmail.length < 1) {
toastr.error(t('error-invalid-from-address'));
return;
}
if (emailBody.indexOf('[unsubscribe]') === -1) {
toastr.error(t('error-missing-unsubscribe-link'));
return;
}
meteorSendMail(fromEmail.value, subject, emailBody, dryRun, query.value);
toastr.success(t('The_emails_are_being_sent'));
};
};
export default function MailerRoute(props) {
const canAccessMailer = usePermission('access-mailer');
const sendMail = useSendMail();
return canAccessMailer ? <Mailer sendMail={sendMail} {...props} /> : <NotAuthorizedPage/>;
}

@ -38,6 +38,11 @@ registerAdminRoute('/info', {
lazyRouteComponent: () => import('./components/info/InformationRoute'),
});
registerAdminRoute('/mailer', {
name: 'admin-mailer',
lazyRouteComponent: () => import('./components/mailer/MailerRoute'),
});
registerAdminRoute('/users', {
name: 'admin-users',
action: async () => {

@ -0,0 +1,7 @@
export const isJSON = (value) => {
try {
return !!JSON.parse(value);
} catch {
return false;
}
};

@ -2743,6 +2743,7 @@
"Purchase_for_price": "Purchase for $%s",
"Query": "Query",
"Query_description": "Additional conditions for determining which users to send the email to. Unsubscribed users are automatically removed from the query. It must be a valid JSON. Example: \"{\"createdAt\":{\"$gt\":{\"$date\": \"2015-01-01T00:00:00.000Z\"}}}\"",
"Query_is_not_valid_JSON": "Query is not valid JSON",
"Queue": "Queue",
"quote": "quote",
"Quote": "Quote",

Loading…
Cancel
Save