More work with titles & descriptions

This commit is contained in:
vjrj 2018-02-07 15:27:12 +01:00
parent a1138c8c43
commit 464af6b9d1
21 changed files with 178 additions and 51 deletions

View file

@ -10,6 +10,16 @@ Feature: Test all secundary pages
| activeFires | Active Fires | | activeFires | Active Fires |
Then I check that all pages works properly Then I check that all pages works properly
Scenario: Check that all secondary pages work well
Given a list of page urls and contents
| login | Login | true |
| signup | Sign Up | true |
| recover-password | Recover Password | true |
| verify-email/something | Verify | true |
| fire/inexistent | This page doesn't exist | false |
| fire/Fe26.2**1a0361ed0384f741403682e26b4bbc3850bee24e775da13c9782b22365ea895f*r_3gmVad5vzkeyqPpo6UcA*1s5fFz3iDGKTYP2RCvXMshof00QCHf4ErDl9K2dxoX0u-J-t6scyOWG8pGp3ehg_FfEtyR_kYcEKU3rE0jaSlZbD09TIvhiIJeS3C6Uc8YD-rit0XBrgsVKfYSxKzTRoOYiYFJ8JYd298hMtfiASePjS05Z58hhicyCcJYYRlarqDScG3LiVY3lL5y2nfcdIMNuSjCiKOJWuMkxwd9nR1UHMudLl0hEoy56mPdnHpDYtP9IYUlIOk1LlWBxcmHKifbXeqHu94p8j13Kk20dh2R49Hw3KsSoE9UbWmGQA9wAZXT82301i3rGF5GPAKjlTlRYcWisQurnPwHSVmx3DhUdiYwKGxt4KeaM5QVI4BE9octvE41OOprB_-Il105diQEh2Y9vdvX51ZVWIRfCboICPM6rJb0Oin7U7F1iM-oD_5s3DGnelfM5LGBcKwiB5paMo5M5vdBMaO-zR216cW9yGVXw9IZqHx8xDQWnoHAZjt8NLHeiGF2QOmIGtEUH7qnwhGpkcvszajmAZzR8saZgoH1qfBfvpVA41YfV14gU**4d030f05e23ad75409cebc609107467fc60be5077d52cf041087cd024fc4dc45*GY97aGFc1MyAsoO3Qxqtgwk9j-MbAPdEGBmEHq6r8VU | Additional information | false |
Then I check that all page urls works properly
Scenario: Check that other non visible pages work well Scenario: Check that other non visible pages work well
Given a list of non visible pages ids and contents Given a list of non visible pages ids and contents
| status | Status | | status | Status |

View file

@ -65,8 +65,8 @@ module.exports = function doSteps(notos) {
*/ */
function closeAlert() { function closeAlert() {
client.waitForVisible('.bert-alert', 5000); // client.waitForVisible('.bert-alert', 5000);
client.click('.bert-content'); // client.click('.bert-content');
client.waitUntil(() => client.isVisible('.bert-alert') === false, 10000); client.waitUntil(() => client.isVisible('.bert-alert') === false, 10000);
} }

View file

@ -17,6 +17,21 @@ module.exports = function () {
client.waitForVisible(id, 10000); client.waitForVisible(id, 10000);
client.click(id); client.click(id);
client.waitForText('#react-root', content); client.waitForText('#react-root', content);
// https://jasmine.github.io/2.3/introduction.html#section-Expectations
expect(client.getTitle()).toContain(pages[i][1]);
}
function processPageUrl(i) {
const url = `${pages[i][0]}`;
const content = pages[i][1];
client.url(`${process.env.ROOT_URL}/${url}`);
client.waitForVisible('#react-root', 10000);
client.waitForText('#react-root', content);
const checkTitle = pages[i][2] === 'true';
// https://jasmine.github.io/2.3/introduction.html#section-Expectations
if (checkTitle) {
expect(client.getTitle()).toContain(pages[i][1]);
}
} }
this.Given(/^a list of non visible pages ids and contents$/, (table, callback) => { this.Given(/^a list of non visible pages ids and contents$/, (table, callback) => {
@ -35,9 +50,25 @@ module.exports = function () {
this.Then(/^I check that all pages works properly$/, (callback) => { this.Then(/^I check that all pages works properly$/, (callback) => {
goHome(client); goHome(client);
if (client.isVisible('#logout')) {
client.click('#logout');
}
for (let i = 0; i < pages.length; i += 1) { for (let i = 0; i < pages.length; i += 1) {
processPage(i); processPage(i);
} }
callback(); callback();
}); });
this.Given(/^a list of page urls and contents$/, (table, callback) => {
pages = table.raw();
callback();
});
this.Then(/^I check that all page urls works properly$/, (callback) => {
goHome(client);
for (let i = 0; i < pages.length; i += 1) {
processPageUrl(i);
}
callback();
});
}; };

View file

@ -24,11 +24,29 @@ const options = {
const geocoder = NodeGeocoder(options); const geocoder = NodeGeocoder(options);
const unseal = async (obj) => {
try {
const unsealed = await urlEnc.decrypt(obj);
return unsealed;
} catch (error) {
// console.warn(error);
return undefined;
}
};
const unsealW = obj => unseal(obj);
Meteor.publish('fireFromHash', function fireFromHash(fireEnc) { Meteor.publish('fireFromHash', function fireFromHash(fireEnc) {
check(fireEnc, String); check(fireEnc, String);
try { try {
// console.log(fireEnc); // console.log(fireEnc);
const unsealed = Promise.await(urlEnc.decrypt(fireEnc)); // const unsealed = Promise.await(urlEnc.decrypt(fireEnc));
const unsealed = Promise.await(unsealW(fireEnc));
if (unsealed === undefined) {
console.info(`Wrong fire: ${fireEnc}`);
// https://guide.meteor.com/data-loading.html
return this.ready();
}
const w = unsealed.when; const w = unsealed.when;
unsealed.when = new Date(w); unsealed.when = new Date(w);
const c = unsealed.createdAt; const c = unsealed.createdAt;
@ -36,7 +54,6 @@ Meteor.publish('fireFromHash', function fireFromHash(fireEnc) {
const u = unsealed.updatedAt; const u = unsealed.updatedAt;
unsealed.updatedAt = !u ? new Date() : new Date(u); unsealed.updatedAt = !u ? new Date() : new Date(u);
// console.log(unsealed); // console.log(unsealed);
// FIXME: // FIXME:
if (typeof unsealed.confidence === 'string') { if (typeof unsealed.confidence === 'string') {
unsealed.confidence = Number.parseInt(unsealed.confidence, 10); unsealed.confidence = Number.parseInt(unsealed.confidence, 10);
@ -55,7 +72,7 @@ Meteor.publish('fireFromHash', function fireFromHash(fireEnc) {
} }
// console.log(unsealed.address); // console.log(unsealed.address);
} catch (reve) { } catch (reve) {
console.error(reve); console.warn(reve);
} }
} }
if (fire.count() === 0) { if (fire.count() === 0) {
@ -65,9 +82,9 @@ Meteor.publish('fireFromHash', function fireFromHash(fireEnc) {
} }
return findFire(unsealed); return findFire(unsealed);
/* console.log(`fires: ${fire.count()}`); /* console.log(`fires: ${fire.count()}`);
* return fire; */ * return fire; */
} catch (e) { } catch (e) {
console.error(e); console.warn(e);
throw new Meteor.Error('500', e); return this.ready();
} }
}); });

View file

@ -0,0 +1,4 @@
/* eslint-env jquery */
// remove static description so it's not duplicated
$('meta[name=description]').remove();

View file

@ -15,7 +15,7 @@ const backOpts = {
jsonIndent: 2 jsonIndent: 2
}; };
const forceDebug = true; const forceDebug = false;
const shouldDebug = (forceDebug && !Meteor.isProduction); const shouldDebug = (forceDebug && !Meteor.isProduction);
const i18nOpts = { const i18nOpts = {

View file

@ -0,0 +1,13 @@
import { Meteor } from 'meteor/meteor';
import { ReactiveVar } from 'meteor/reactive-var';
export const location = new ReactiveVar();
export const currentLocation = () => {
if (Meteor.isClient) {
return window.location.pathname;
}
return location.get();
};
export const isHome = () => currentLocation() === '/';

View file

@ -14,6 +14,7 @@ import { Roles } from 'meteor/alanning:roles';
import Blaze from 'meteor/gadicc:blaze-react-component'; import Blaze from 'meteor/gadicc:blaze-react-component';
// i18n // i18n
import i18n, { i18nReady } from '/imports/startup/client/i18n'; import i18n, { i18nReady } from '/imports/startup/client/i18n';
import '/imports/startup/client/meta';
import '/imports/startup/client/ravenLogger'; import '/imports/startup/client/ravenLogger';
import '/imports/startup/client/geolocation'; import '/imports/startup/client/geolocation';
import '/imports/startup/client/piwik-start.js'; import '/imports/startup/client/piwik-start.js';

View file

@ -25,21 +25,25 @@ class Fire extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
loading: props.loading,
notfound: props.notfound,
when: props.when when: props.when
}; };
} }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
if (this.props.when !== nextProps.when) { if (this.props.when !== nextProps.when || this.props.loading !== nextProps.loading || this.props.notfound !== nextProps.notfound) {
// console.log(`Next when ${nextProps.when}`); // console.log(`Next when ${nextProps.when}`);
this.setState({ this.setState({
loading: nextProps.loading,
notfound: nextProps.notfound,
when: nextProps.when when: nextProps.when
}); });
} }
} }
shouldComponentUpdate(nextProps, nextState) { shouldComponentUpdate(nextProps, nextState) {
return !(nextState.when === this.state.when); return !(nextState.when === this.state.when && nextState.loading === this.state.loading && this.state.notfound === nextState.notfound);
} }
onTypeSelect(key) { onTypeSelect(key) {
@ -55,23 +59,27 @@ class Fire extends React.Component {
} }
render() { render() {
const { loading, fire, t } = this.props; const {
notfound, loading, fire, t
} = this.props;
/* console.log(`loading fire: ${loading}`);
* console.log(`Not found fire: ${notfound}`); */
if (fire && fire.when) { if (fire && fire.when) {
this.dateLongFormat = dateLongFormat(fire.when); this.dateLongFormat = dateLongFormat(fire.when);
this.title = fire.address ?
t('Información adicional sobre fuego detectado en {{where}} el {{when}}', { where: fire.address, when: this.dateLongFormat }) :
t('Información adicional sobre fuego detectado el {{when}}', { when: this.dateLongFormat });
} }
const title = fire.address ?
t('Información adicional sobre fuego detectado en {{where}} el {{when}}', { where: fire.address, when: this.dateLongFormat }) : return (fire && !loading ? (
t('Información adicional sobre fuego detectado el {{when}}', { when: this.dateLongFormat });
return (fire ? (
<div className="ViewFire"> <div className="ViewFire">
<Helmet> <Helmet>
<meta charSet="utf-8" />
<title>{t('AppName')}: {t('Información adicional sobre fuego')}</title> <title>{t('AppName')}: {t('Información adicional sobre fuego')}</title>
<meta name="description" content={title} /> <meta name="description" content={this.title} />
</Helmet> </Helmet>
{!loading && {!loading &&
<Fragment> <Fragment>
<h4 className="page-header">{title}</h4> <h4 className="page-header">{this.title}</h4>
<Map <Map
ref={(map) => { ref={(map) => {
this.fireMap = map; this.fireMap = map;
@ -147,13 +155,14 @@ class Fire extends React.Component {
</Fragment> </Fragment>
} }
</div> </div>
) : <NotFound />); ) : <Fragment>{ notfound && <NotFound /> }</Fragment>);
} }
} }
Fire.propTypes = { Fire.propTypes = {
t: PropTypes.func.isRequired, t: PropTypes.func.isRequired,
loading: PropTypes.bool.isRequired, loading: PropTypes.bool.isRequired,
notfound: PropTypes.bool.isRequired,
when: PropTypes.instanceOf(Date), when: PropTypes.instanceOf(Date),
fire: PropTypes.object fire: PropTypes.object
}; };
@ -167,10 +176,15 @@ const FireContainer = withTracker(({ match }) => {
const fireEncrypt = match.params.id; const fireEncrypt = match.params.id;
const subscription = Meteor.subscribe('fireFromHash', fireEncrypt); const subscription = Meteor.subscribe('fireFromHash', fireEncrypt);
// console.log(`Subs ready: ${subscription.ready()}, fire: ${JSON.stringify(FiresCollection.findOne())}`); // console.log(`Subs ready: ${subscription.ready()}, fire: ${JSON.stringify(FiresCollection.findOne())}`);
const loading = !subscription.ready();
const notfound = !loading && FiresCollection.find().count() === 0;
/* console.log(`loading fire: ${loading}`);
* console.log(`Not found fire: ${notfound}`); */
return { return {
loading: !subscription.ready(), loading,
fire: FiresCollection.findOne(), fire: FiresCollection.findOne(),
when: subscription.ready() ? FiresCollection.findOne().when : null notfound,
when: subscription.ready() && FiresCollection.findOne() ? FiresCollection.findOne().when : null
}; };
})(Fire); })(Fire);

View file

@ -29,6 +29,7 @@ import FireAlertsCollection from '/imports/api/FireAlerts/FireAlerts';
import SiteSettings from '/imports/api/SiteSettings/SiteSettings'; import SiteSettings from '/imports/api/SiteSettings/SiteSettings';
import UserSubsToFiresCollection from '/imports/api/Subscriptions/Subscriptions'; import UserSubsToFiresCollection from '/imports/api/Subscriptions/Subscriptions';
import { isNotHomeAndMobile, isChrome } from '/imports/ui/components/Utils/isMobile'; import { isNotHomeAndMobile, isChrome } from '/imports/ui/components/Utils/isMobile';
import { isHome } from '/imports/ui/components/Utils/location';
import './FiresMap.scss'; import './FiresMap.scss';
@ -110,18 +111,17 @@ class FiresMap extends React.Component {
} }
handleLeafletLoad(map) { handleLeafletLoad(map) {
// console.log(map); if (map && map.leafletElement) {
if (map) { const lmap = map.leafletElement;
// console.log('Firesmap loading');
try { try {
const bounds = this.getMap().getBounds(); const bounds = lmap.getBounds();
mapSize.set([bounds.getNorthEast(), bounds.getSouthWest()]); mapSize.set([bounds.getNorthEast(), bounds.getSouthWest()]);
if (!this.state.scaleAdded) { if (!this.state.scaleAdded) {
this.addScale(); this.addScale();
this.state.scaleAdded = true; this.state.scaleAdded = true;
} }
} catch (e) { } catch (e) {
console.log('Failed to set map bounds and scale'); console.warn('Failed to set map bounds and scale');
} }
this.state.union = subsUnion(this.state.union, { this.state.union = subsUnion(this.state.union, {
map, map,
@ -134,15 +134,14 @@ class FiresMap extends React.Component {
render() { render() {
const { t } = this.props; const { t } = this.props;
console.log(`Rendering ${this.props.loading ? 'loading' : 'LOADED'} map ${this.props.activefires.length + this.props.firealerts.length} of ${this.props.activefirestotal} total. Subs users ready ${this.props.subsready}, reactive ${this.state.viewport.zoom >= MAXZOOMREACTIVE}`); console.log(`Rendering ${this.props.loading ? 'loading' : 'LOADED'} map ${this.props.activefires.length + this.props.firealerts.length} of ${this.props.activefirestotal} total. Subs users ready ${this.props.subsready} (${this.props.userSubs.length}), reactive ${this.state.viewport.zoom >= MAXZOOMREACTIVE}`);
return ( return (
/* Large number of markers: /* Large number of markers:
https://stackoverflow.com/questions/43015854/large-dataset-of-markers-or-dots-in-leaflet/43019740#43019740 */ https://stackoverflow.com/questions/43015854/large-dataset-of-markers-or-dots-in-leaflet/43019740#43019740 */
<div <div
ref={(divElement) => { this.divElement = divElement; }} ref={(divElement) => { this.divElement = divElement; }}
> >
{ window.location.pathname !== '/' && { !isHome() &&
<Helmet> <Helmet>
<title>{t('AppName')}: {t('Fuegos activos')}</title> <title>{t('AppName')}: {t('Fuegos activos')}</title>
<meta name="description" content={t('Fuegos activos en el mundo actualizados en tiempo real')} /> <meta name="description" content={t('Fuegos activos en el mundo actualizados en tiempo real')} />
@ -199,7 +198,7 @@ class FiresMap extends React.Component {
onClick={this.onClickReset} onClick={this.onClickReset}
viewport={this.state.viewport} viewport={this.state.viewport}
onViewportChanged={this.onViewportChanged} onViewportChanged={this.onViewportChanged}
sleep={window.location.pathname === '/' && !isChrome} sleep={isHome() && !isChrome}
sleepTime={10750} sleepTime={10750}
wakeTime={750} wakeTime={750}
sleepNote sleepNote

View file

@ -6,6 +6,7 @@ import { Row, FormGroup, ControlLabel, Button } from 'react-bootstrap';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { Meteor } from 'meteor/meteor'; import { Meteor } from 'meteor/meteor';
import { Bert } from 'meteor/themeteorchef:bert'; import { Bert } from 'meteor/themeteorchef:bert';
import { Helmet } from 'react-helmet';
import { translate } from 'react-i18next'; import { translate } from 'react-i18next';
import { T9n } from 'meteor-accounts-t9n'; import { T9n } from 'meteor-accounts-t9n';
import { testId } from '/imports/ui/components/Utils/TestUtils'; import { testId } from '/imports/ui/components/Utils/TestUtils';
@ -68,6 +69,9 @@ class Login extends React.Component {
render() { render() {
return ( return (
<div className="Login"> <div className="Login">
<Helmet>
<title>{this.t('AppName')}: {this.t('Iniciar sesión')}</title>
</Helmet>
<Row className="align-items-center justify-content-center"> <Row className="align-items-center justify-content-center">
<Col xs={12} sm={6} md={5} lg={4}> <Col xs={12} sm={6} md={5} lg={4}>
<h4 className="page-header">{this.t('Iniciar sesión')}</h4> <h4 className="page-header">{this.t('Iniciar sesión')}</h4>

View file

@ -1,6 +1,8 @@
import React from 'react'; import React from 'react';
import { Meteor } from 'meteor/meteor';
import Icon from '../../components/Icon/Icon'; import Icon from '../../components/Icon/Icon';
import { translate, Trans } from 'react-i18next'; import { translate, Trans } from 'react-i18next';
import { Helmet } from 'react-helmet';
import './Logout.scss'; import './Logout.scss';
@ -12,6 +14,9 @@ class Logout extends React.Component {
render() { render() {
return ( return (
<div className="Logout"> <div className="Logout">
<Helmet>
<title>{this.props.t('AppName')}: {this.props.t('Logout')}</title>
</Helmet>
<h1><Trans parent="span">Gracias por Participar</Trans></h1> <h1><Trans parent="span">Gracias por Participar</Trans></h1>
<p><Trans parent="span">También puedes seguirnos en la web</Trans></p> <p><Trans parent="span">También puedes seguirnos en la web</Trans></p>
<ul className="FollowUsElsewhere"> <ul className="FollowUsElsewhere">

View file

@ -1,9 +1,13 @@
import React from 'react'; import React from 'react';
import { Alert } from 'react-bootstrap'; import { Alert } from 'react-bootstrap';
import { translate, Trans } from 'react-i18next'; import { translate, Trans } from 'react-i18next';
import { Helmet } from 'react-helmet';
const NotFound = () => ( const NotFound = () => (
<div className="NotFound"> <div className="NotFound">
<Helmet>
<title>This page doesn't exist"</title>
</Helmet>
<Alert bsStyle="danger"> <Alert bsStyle="danger">
<p> <p>
<Trans i18nKey="not-found">Upppps: Esta página no existe</Trans> <Trans i18nKey="not-found">Upppps: Esta página no existe</Trans>

View file

@ -15,7 +15,6 @@ const Page = ({ title, subtitle, content }) => (
<Helmet> <Helmet>
<meta charSet="utf-8" /> <meta charSet="utf-8" />
<title>{i18n.t('AppName')}: {title}</title> <title>{i18n.t('AppName')}: {title}</title>
<meta name="description" content={content.substr(0, 100).lastIndexOf(' ')} />
</Helmet> </Helmet>
<PageHeader title={title} subtitle={subtitle} /> <PageHeader title={title} subtitle={subtitle} />
<Content content={content} /> <Content content={content} />

View file

@ -5,6 +5,7 @@ import PropTypes from 'prop-types';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { Accounts } from 'meteor/accounts-base'; import { Accounts } from 'meteor/accounts-base';
import { Bert } from 'meteor/themeteorchef:bert'; import { Bert } from 'meteor/themeteorchef:bert';
import { Helmet } from 'react-helmet';
import AccountPageFooter from '../../components/AccountPageFooter/AccountPageFooter'; import AccountPageFooter from '../../components/AccountPageFooter/AccountPageFooter';
import validate from '../../../modules/validate'; import validate from '../../../modules/validate';
import { translate } from 'react-i18next'; import { translate } from 'react-i18next';
@ -24,16 +25,16 @@ class RecoverPassword extends React.Component {
rules: { rules: {
emailAddress: { emailAddress: {
required: true, required: true,
email: true, email: true
}, }
}, },
messages: { messages: {
emailAddress: { emailAddress: {
required: this.t("Necesitamos un correo aquí."), required: this.t('Necesitamos un correo aquí.'),
email: this.t("¿Es este correo correcto?"), email: this.t('¿Es este correo correcto?')
}, }
}, },
submitHandler() { component.handleSubmit(); }, submitHandler() { component.handleSubmit(); }
}); });
} }
@ -46,7 +47,7 @@ class RecoverPassword extends React.Component {
if (error) { if (error) {
Bert.alert(T9n.get(`error.accounts.${error.reason}`), 'danger'); Bert.alert(T9n.get(`error.accounts.${error.reason}`), 'danger');
} else { } else {
Bert.alert(t("checkResetEmail", {email: email}), 'success'); Bert.alert(t('checkResetEmail', { email }), 'success');
history.push('/login'); history.push('/login');
} }
}); });
@ -55,14 +56,17 @@ class RecoverPassword extends React.Component {
render() { render() {
return (<div className="RecoverPassword"> return (<div className="RecoverPassword">
<Row className="align-items-center justify-content-center"> <Row className="align-items-center justify-content-center">
<Helmet>
<title>{this.t('AppName')}: {this.t('Recupera tu contraseña')}</title>
</Helmet>
<Col xs={12} sm={6} md={5} lg={4}> <Col xs={12} sm={6} md={5} lg={4}>
<h4 className="page-header">{this.t("Recupera tu contraseña")}</h4> <h4 className="page-header">{this.t('Recupera tu contraseña')}</h4>
<Alert bsStyle="info"> <Alert bsStyle="info">
{this.t("Introduce tu correo abajo para recibir un enlace para resetear tu contraseña.")} {this.t('Introduce tu correo abajo para recibir un enlace para resetear tu contraseña.')}
</Alert> </Alert>
<form ref={form => (this.form = form)} onSubmit={event => event.preventDefault()}> <form ref={form => (this.form = form)} onSubmit={event => event.preventDefault()}>
<FormGroup> <FormGroup>
<ControlLabel>{this.t("Correo electrónico")}</ControlLabel> <ControlLabel>{this.t('Correo electrónico')}</ControlLabel>
<input <input
type="email" type="email"
name="emailAddress" name="emailAddress"
@ -70,9 +74,9 @@ class RecoverPassword extends React.Component {
className="form-control" className="form-control"
/> />
</FormGroup> </FormGroup>
<Button type="submit" bsStyle="success">{this.t("Recupera tu contraseña")}</Button> <Button type="submit" bsStyle="success">{this.t('Recupera tu contraseña')}</Button>
<AccountPageFooter> <AccountPageFooter>
<p>{this.t("¿Recuerdas tu contraseña?")} <Link to="/login">{this.t("Iniciar sesión")}</Link>.</p> <p>{this.t('¿Recuerdas tu contraseña?')} <Link to="/login">{this.t('Iniciar sesión')}</Link>.</p>
</AccountPageFooter> </AccountPageFooter>
</form> </form>
</Col> </Col>
@ -82,7 +86,7 @@ class RecoverPassword extends React.Component {
} }
RecoverPassword.propTypes = { RecoverPassword.propTypes = {
history: PropTypes.object.isRequired, history: PropTypes.object.isRequired
}; };
export default translate([], { wait: true })(RecoverPassword); export default translate([], { wait: true })(RecoverPassword);

View file

@ -4,6 +4,7 @@ import { Row, Alert, FormGroup, ControlLabel, Button } from 'react-bootstrap';
import Col from '../../components/Col/Col'; import Col from '../../components/Col/Col';
import { Accounts } from 'meteor/accounts-base'; import { Accounts } from 'meteor/accounts-base';
import { Bert } from 'meteor/themeteorchef:bert'; import { Bert } from 'meteor/themeteorchef:bert';
import { Helmet } from 'react-helmet';
import validate from '../../../modules/validate'; import validate from '../../../modules/validate';
import { translate } from 'react-i18next'; import { translate } from 'react-i18next';
import { T9n } from 'meteor-accounts-t9n'; import { T9n } from 'meteor-accounts-t9n';
@ -61,6 +62,9 @@ class ResetPassword extends React.Component {
render() { render() {
return (<div className="ResetPassword"> return (<div className="ResetPassword">
<Row className="align-items-center justify-content-center"> <Row className="align-items-center justify-content-center">
<Helmet>
<title>{this.t('AppName')}: {this.t('Resetea tu contraseña')}</title>
</Helmet>
<Col xs={12} sm={6} md={4}> <Col xs={12} sm={6} md={4}>
<h4 className="page-header">{this.t("Resetea tu contraseña")}</h4> <h4 className="page-header">{this.t("Resetea tu contraseña")}</h4>
<Alert bsStyle="info"> <Alert bsStyle="info">

View file

@ -1,22 +1,24 @@
/* eslint-disable react/jsx-indent-props */ /* eslint-disable react/jsx-indent-props */
/* eslint-disable import/no-absolute-path */
import React from 'react'; import React from 'react';
import { Row, FormGroup, ControlLabel, Button, Checkbox } from 'react-bootstrap'; import { Row, FormGroup, ControlLabel, Button, Checkbox } from 'react-bootstrap';
import Col from '../../components/Col/Col';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { Meteor } from 'meteor/meteor'; import { Meteor } from 'meteor/meteor';
import { Accounts } from 'meteor/accounts-base'; import { Accounts } from 'meteor/accounts-base';
import { Bert } from 'meteor/themeteorchef:bert'; import { Bert } from 'meteor/themeteorchef:bert';
import { translate, Trans } from 'react-i18next';
import { T9n } from 'meteor-accounts-t9n';
import { Helmet } from 'react-helmet';
import { testId } from '/imports/ui/components/Utils/TestUtils'; import { testId } from '/imports/ui/components/Utils/TestUtils';
import Col from '../../components/Col/Col';
import Icon from '../../components/Icon/Icon'; import Icon from '../../components/Icon/Icon';
import OAuthLoginButtons from '../../components/OAuthLoginButtons/OAuthLoginButtons'; import OAuthLoginButtons from '../../components/OAuthLoginButtons/OAuthLoginButtons';
import InputHint from '../../components/InputHint/InputHint'; import InputHint from '../../components/InputHint/InputHint';
import AccountPageFooter from '../../components/AccountPageFooter/AccountPageFooter'; import AccountPageFooter from '../../components/AccountPageFooter/AccountPageFooter';
import validate from '../../../modules/validate'; import validate from '../../../modules/validate';
import './Signup.scss'; import './Signup.scss';
import { translate, Trans } from 'react-i18next';
import { T9n } from 'meteor-accounts-t9n';
class Signup extends React.Component { class Signup extends React.Component {
constructor(props) { constructor(props) {
@ -120,6 +122,9 @@ class Signup extends React.Component {
const { t, history } = this.props; const { t, history } = this.props;
return (<div className="Signup"> return (<div className="Signup">
<Row className="align-items-center justify-content-center"> <Row className="align-items-center justify-content-center">
<Helmet>
<title>{this.t('AppName')}: {this.t('Registrarse')}</title>
</Helmet>
<Col xs={12} sm={6} md={5} lg={4}> <Col xs={12} sm={6} md={5} lg={4}>
<h4 className="page-header">{t('Registrarse')}</h4> <h4 className="page-header">{t('Registrarse')}</h4>
<Row> <Row>

View file

@ -7,6 +7,7 @@ import PropTypes from 'prop-types';
import { translate } from 'react-i18next'; import { translate } from 'react-i18next';
import { withTracker } from 'meteor/react-meteor-data'; import { withTracker } from 'meteor/react-meteor-data';
import Blaze from 'meteor/gadicc:blaze-react-component'; import Blaze from 'meteor/gadicc:blaze-react-component';
import { Helmet } from 'react-helmet';
/* /*
https://github.com/meteor/docs/blob/version-NEXT/long-form/oplog-observe-driver.md https://github.com/meteor/docs/blob/version-NEXT/long-form/oplog-observe-driver.md
@ -26,6 +27,9 @@ class Status extends Component {
render() { render() {
return ( return (
<Fragment> <Fragment>
<Helmet>
<title>{this.t('AppName')}: Status</title>
</Helmet>
<h4 className="page-header">Status</h4> <h4 className="page-header">Status</h4>
<Blaze template="serverFacts" /> <Blaze template="serverFacts" />
</Fragment> </Fragment>

View file

@ -9,6 +9,7 @@ import { Meteor } from 'meteor/meteor';
import { withTracker } from 'meteor/react-meteor-data'; import { withTracker } from 'meteor/react-meteor-data';
import { Trans, translate } from 'react-i18next'; import { Trans, translate } from 'react-i18next';
import { Bert } from 'meteor/themeteorchef:bert'; import { Bert } from 'meteor/themeteorchef:bert';
import { Helmet } from 'react-helmet';
import UserSubsToFiresCollection from '/imports/api/Subscriptions/Subscriptions'; import UserSubsToFiresCollection from '/imports/api/Subscriptions/Subscriptions';
import SelectionMap, { action } from '/imports/ui/components/SelectionMap/SelectionMap'; import SelectionMap, { action } from '/imports/ui/components/SelectionMap/SelectionMap';
import confirm from '/imports/ui/components/Prompt/Confirm'; import confirm from '/imports/ui/components/Prompt/Confirm';
@ -84,6 +85,9 @@ class Subscriptions extends Component {
const firstBtnTitle = ['Añadir zona', '', 'Terminar']; // view, add, edit const firstBtnTitle = ['Añadir zona', '', 'Terminar']; // view, add, edit
return (!loading ? ( return (!loading ? (
<div className="Subscriptions"> <div className="Subscriptions">
<Helmet>
<title>{t('AppName')}: {t('Suscripciones a alertas de fuegos en zonas de mi interés')}</title>
</Helmet>
<h4 className="page-header"><Trans>Suscripciones a alertas de fuegos en zonas de mi interés</Trans></h4> <h4 className="page-header"><Trans>Suscripciones a alertas de fuegos en zonas de mi interés</Trans></h4>
{ subscriptions.length === 0 ? { subscriptions.length === 0 ?
<Alert bsStyle="warning"><Trans>No estás suscrito a fuegos en ninguna zona</Trans></Alert> : <Alert bsStyle="warning"><Trans>No estás suscrito a fuegos en ninguna zona</Trans></Alert> :

View file

@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Alert } from 'react-bootstrap'; import { Alert } from 'react-bootstrap';
import { Meteor } from 'meteor/meteor'; import { Meteor } from 'meteor/meteor';
import { Helmet } from 'react-helmet';
import { Accounts } from 'meteor/accounts-base'; import { Accounts } from 'meteor/accounts-base';
import { Bert } from 'meteor/themeteorchef:bert'; import { Bert } from 'meteor/themeteorchef:bert';
import { translate } from 'react-i18next'; import { translate } from 'react-i18next';
@ -32,6 +33,9 @@ class VerifyEmail extends React.Component {
render() { render() {
return (<div className="VerifyEmail"> return (<div className="VerifyEmail">
<Helmet>
<title>{this.t('AppName')}: {this.t('Verifica tu dirección de correo')}</title>
</Helmet>
<Alert bsStyle={!this.state.error ? 'info' : 'danger'}> <Alert bsStyle={!this.state.error ? 'info' : 'danger'}>
{!this.state.error ? this.t('Verificando...') : this.state.error} {!this.state.error ? this.t('Verificando...') : this.state.error}
</Alert> </Alert>

View file

@ -66,8 +66,8 @@
"Uso de Cookies": "Use of Cookies", "Uso de Cookies": "Use of Cookies",
"Utilizamos cookies para asegurar un mejor uso de nuestra web. Si continúas navegando, consideramos que aceptas su uso": "We use cookies to ensure a better use of our website. If you continue browsing, we consider that you accept their use", "Utilizamos cookies para asegurar un mejor uso de nuestra web. Si continúas navegando, consideramos que aceptas su uso": "We use cookies to ensure a better use of our website. If you continue browsing, we consider that you accept their use",
"Participa": "Get Involved", "Participa": "Get Involved",
"activeFires": "Active fires", "activeFires": "Active Fires",
"Fuegos activos": "Active fires", "Fuegos activos": "Active Fires",
"noActiveFireInMapCount": "There are no active fires in this area of the map. There is a total of <1><0>{{countTotal}}</0></1> active fires detected worldwide.", "noActiveFireInMapCount": "There are no active fires in this area of the map. There is a total of <1><0>{{countTotal}}</0></1> active fires detected worldwide.",
"activeFireInMapCount": "On the map marked in red <1><0>{{count,number}}</0></1> active fires. There is a total of <3><0>{{countTotal,number}} </0></3> active fires detected worldwide by NASA.", "activeFireInMapCount": "On the map marked in red <1><0>{{count,number}}</0></1> active fires. There is a total of <3><0>{{countTotal,number}} </0></3> active fires detected worldwide by NASA.",
"activeNeigFireInMapCount": "In Orange, the fires recently reported by our users.", "activeNeigFireInMapCount": "In Orange, the fires recently reported by our users.",
@ -161,5 +161,6 @@
"Verifica tu dirección de correo": "Verify Your Email Address", "Verifica tu dirección de correo": "Verify Your Email Address",
"Zonas vigiladas": "Monitored areas", "Zonas vigiladas": "Monitored areas",
"En verde, las zonas vigiladas por nuestros usuari@s actualmente": "In green, the areas monitored by our users currently", "En verde, las zonas vigiladas por nuestros usuari@s actualmente": "In green, the areas monitored by our users currently",
"Datos actualizados <1></1>.": "Data updated <1></1>." "Datos actualizados <1></1>.": "Data updated <1></1>.",
"Información adicional sobre fuego": "Additional information about fire"
} }