Skip to content

Commit

Permalink
Add StatsTimestamp component
Browse files Browse the repository at this point in the history
- Introduce a new component, `StatsTimestamp`, responsible for displaying timestamps with tooltips. This component accepts a `messageType` prop to determine whether to show a generic or project-specific message. The inconsistent icon usage has been resolved, now consistently using an info icon.
- Reorganized the codebase by removing timestamp implementations from the User Contributions and Project Stats components. Instead, these components now utilize the newly introduced `StatsTimestamp` component for timestamp display.
- Integrated the `StatsTimestamp` component into the Total Features section of the overall statistics page.
  • Loading branch information
HelNershingThapa committed Aug 21, 2023
1 parent c6a4bb0 commit 0c30aea
Show file tree
Hide file tree
Showing 11 changed files with 111 additions and 69 deletions.
21 changes: 9 additions & 12 deletions frontend/src/components/projectStats/edits.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,26 @@
import React from 'react';
import ReactTooltip from 'react-tooltip';
import { FormattedMessage, useIntl } from 'react-intl';
import { FormattedMessage } from 'react-intl';

import projectMessages from './messages';
import userDetailMessages from '../userDetail/messages';
import { MappingIcon, HomeIcon, RoadIcon, EditIcon, InfoIcon } from '../svgIcons';
import { MappingIcon, HomeIcon, RoadIcon, EditIcon } from '../svgIcons';
import { StatsCard } from '../statsCard';
import StatsTimestamp from '../statsTimestamp';

export const EditsStats = ({ data }) => {
const intl = useIntl();
const { changesets, buildings, roads, edits } = data;

const iconClass = 'h-50 w-50';
const iconStyle = { height: '45px' };

return (
<div className="cf w-100 pb4 ph2 ph4-ns blue-dark">
<h3 className="barlow-condensed ttu f3">
<FormattedMessage {...projectMessages.edits} />
<InfoIcon
data-tip={intl.formatMessage(projectMessages.editsStats)}
className="blue-grey h1 w1 v-mid pb1 ml2"
/>
</h3>
<ReactTooltip place="top" className="mw6" effect="solid" />
<div className="flex items-center">
<h3 className="barlow-condensed ttu f3">
<FormattedMessage {...projectMessages.edits} />
</h3>
<StatsTimestamp messageType="project" />
</div>
<div className="db pb2 project-edit-stats">
<StatsCard
field={'changesets'}
Expand Down
4 changes: 0 additions & 4 deletions frontend/src/components/projectStats/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,4 @@ export default defineMessages({
id: 'project.stats.edits',
defaultMessage: 'Edits',
},
editsStats: {
id: 'project.stats.edits.info',
defaultMessage: 'These stats are retrieved using the default changeset comment of the project',
},
});
44 changes: 44 additions & 0 deletions frontend/src/components/statsTimestamp/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useState, useEffect } from 'react';
import { useIntl } from 'react-intl';
import ReactTooltip from 'react-tooltip';

import { fetchExternalJSONAPI } from '../../network/genericJSONRequest';
import { OHSOME_STATS_BASE_URL } from '../../config';
import { InfoIcon } from '../svgIcons';
import messages from './messages';

function StatsTimestamp({ messageType }) {
const intl = useIntl();
const [lastUpdated, setLastUpdated] = useState(null);

useEffect(() => {
fetchExternalJSONAPI(`${OHSOME_STATS_BASE_URL}/metadata`)
.then((res) => {
setLastUpdated(res.result.max_timestamp);
})
.catch((error) => console.error(error));
}, []);

const dateOptions = {
year: 'numeric',
month: 'short',
day: '2-digit',
hour: 'numeric',
minute: 'numeric',
};

return (
<div>
<InfoIcon
className="blue-grey h1 w1 v-mid ml2 pointer"
data-tip={intl.formatMessage(messages[messageType], {
formattedDate: intl.formatDate(lastUpdated, dateOptions),
})}
data-for="ohsome-timestamp"
/>
<ReactTooltip id="ohsome-timestamp" place="top" className="mw6" effect="solid" />
</div>
);
}

export default StatsTimestamp;
13 changes: 13 additions & 0 deletions frontend/src/components/statsTimestamp/messages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { defineMessages } from 'react-intl';

export default defineMessages({
generic: {
id: 'stats.ohsome.timestamp.generic',
defaultMessage: 'These statistics were last updated at {formattedDate}',
},
project: {
id: 'stats.ohsome.timestamp.project',
defaultMessage:
'These stats were retrieved using the default changeset comment of the project and were last updated at {formattedDate}',
},
});
62 changes: 36 additions & 26 deletions frontend/src/components/teamsAndOrgs/featureStats.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import React, { useEffect, useState } from 'react';
import axios from 'axios';
import { FormattedMessage } from 'react-intl';

import messages from './messages';
import userDetailMessages from '../userDetail/messages';
import { OHSOME_STATS_BASE_URL } from '../../config';
import { RoadIcon, HomeIcon, WavesIcon, MarkerIcon } from '../svgIcons';
import { StatsCard } from '../statsCard';
import StatsTimestamp from '../statsTimestamp';

export const FeatureStats = () => {
const [stats, setStats] = useState({ edits: 0, buildings: 0, roads: 0, pois: 0, waterways: 0 });
Expand Down Expand Up @@ -35,31 +37,39 @@ export const FeatureStats = () => {
const iconStyle = { height: '45px' };

return (
<div className="w-100 cf">
<StatsCard
icon={<HomeIcon className={iconClass} style={iconStyle} />}
description={<FormattedMessage {...userDetailMessages.buildingsMapped} />}
value={stats.buildings || 0}
className={'w-25-l w-50-m w-100 mv1'}
/>
<StatsCard
icon={<RoadIcon className={iconClass} style={iconStyle} />}
description={<FormattedMessage {...userDetailMessages.roadMapped} />}
value={stats.roads || 0}
className={'w-25-l w-50-m w-100 mv1'}
/>
<StatsCard
icon={<MarkerIcon className={iconClass} style={iconStyle} />}
description={<FormattedMessage {...userDetailMessages.poiMapped} />}
value={stats.pois || 0}
className={'w-25-l w-50-m w-100 mv1'}
/>
<StatsCard
icon={<WavesIcon className={iconClass} style={iconStyle} />}
description={<FormattedMessage {...userDetailMessages.waterwaysMapped} />}
value={stats.waterways || 0}
className={'w-25-l w-50-m w-100 mv1'}
/>
</div>
<>
<div className="flex items-center">
<h4 className="f3 fw6 ttu barlow-condensed blue-dark">
<FormattedMessage {...messages.totalFeatures} />
</h4>
<StatsTimestamp messageType="generic" />
</div>
<div className="w-100 cf">
<StatsCard
icon={<HomeIcon className={iconClass} style={iconStyle} />}
description={<FormattedMessage {...userDetailMessages.buildingsMapped} />}
value={stats.buildings || 0}
className={'w-25-l w-50-m w-100 mv1'}
/>
<StatsCard
icon={<RoadIcon className={iconClass} style={iconStyle} />}
description={<FormattedMessage {...userDetailMessages.roadMapped} />}
value={stats.roads || 0}
className={'w-25-l w-50-m w-100 mv1'}
/>
<StatsCard
icon={<MarkerIcon className={iconClass} style={iconStyle} />}
description={<FormattedMessage {...userDetailMessages.poiMapped} />}
value={stats.pois || 0}
className={'w-25-l w-50-m w-100 mv1'}
/>
<StatsCard
icon={<WavesIcon className={iconClass} style={iconStyle} />}
description={<FormattedMessage {...userDetailMessages.waterwaysMapped} />}
value={stats.waterways || 0}
className={'w-25-l w-50-m w-100 mv1'}
/>
</div>
</>
);
};
4 changes: 4 additions & 0 deletions frontend/src/components/teamsAndOrgs/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -549,4 +549,8 @@ export default defineMessages({
id: 'management.stats.overview',
defaultMessage: 'Overview',
},
totalFeatures: {
id: 'management.stats.features',
defaultMessage: 'Total features',
},
});
14 changes: 2 additions & 12 deletions frontend/src/components/userDetail/elementsMapped.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from 'react';
import humanizeDuration from 'humanize-duration';
import ReactTooltip from 'react-tooltip';
import { FormattedMessage } from 'react-intl';

import messages from './messages';
Expand All @@ -10,11 +9,11 @@ import {
HomeIcon,
WavesIcon,
MarkerIcon,
QuestionCircleIcon,
MappedIcon,
ValidatedIcon,
} from '../svgIcons';
import { StatsCard } from '../statsCard';
import StatsTimestamp from '../statsTimestamp';

export const TaskStats = ({ userStats, username }) => {
const {
Expand Down Expand Up @@ -156,16 +155,7 @@ export const ElementsMapped = ({ userStats, osmStats }) => {
/>
</div>
<div className="cf w-100 relative tr pt3 pr3">
<FormattedMessage {...messages.delayPopup}>
{(msg) => (
<QuestionCircleIcon
className="pointer dib v-mid pl2 pb1 blue-light"
height="1.25rem"
data-tip={msg}
/>
)}
</FormattedMessage>
<ReactTooltip />
<StatsTimestamp messageType="generic" />
</div>
</div>
);
Expand Down
5 changes: 0 additions & 5 deletions frontend/src/components/userDetail/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,6 @@ export default defineMessages({
id: 'users.detail.heatmapLegendLess',
defaultMessage: 'less',
},
delayPopup: {
id: 'users.detail.delay_popup',
defaultMessage:
'These statistics need heavy calculations and changes are showing up with a delay of around one hour.',
},
teams: {
id: 'users.detail.teams',
defaultMessage: 'Teams',
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,8 @@
"project.stats.totalEdits": "Total map edits",
"project.stats.changesets": "Changesets",
"project.stats.edits": "Edits",
"project.stats.edits.info": "These stats are retrieved using the default changeset comment of the project",
"stats.ohsome.timestamp.generic": "These statistics were last updated at {formattedDate}",
"stats.ohsome.timestamp.project": "These stats were retrieved using the default changeset comment of the project and were last updated at {formattedDate}",
"project.tasks.unsaved_map_changes.title": "You have some unsaved map changes",
"project.tasks.unsaved_map_changes.split": "Save or undo it to be able to split the task",
"project.tasks.unsaved_map_changes.unlock": "Save or undo it to be able to select another task",
Expand Down Expand Up @@ -944,6 +945,7 @@
"management.stats.new_users.email_verified": "Confirmed email address",
"management.stats.title": "Statistics",
"management.stats.overview": "Overview",
"management.stats.features": "Total features",
"user.nextLevel": "{changesets} / {nextLevelThreshold} changesets to {level}",
"user.personalInfo": "Personal information",
"user.name": "Name",
Expand Down Expand Up @@ -1064,7 +1066,6 @@
"users.detail.heatmapContributions": "contributions",
"users.detail.heatmapLegendMore": "more",
"users.detail.heatmapLegendLess": "less",
"users.detail.delay_popup": "These statistics need heavy calculations and changes are showing up with a delay of around one hour.",
"users.detail.teams": "Teams",
"error.page.title": "An error occurred",
"error.page.description": "Something did not work well...",
Expand All @@ -1082,7 +1083,6 @@
"management.managers": "Managers",
"management.users.title": "Manage users",
"management.stats.users.title": "New users",
"management.stats.features": "Total features",
"teamsAndOrgs.management.organisation.creation": "Create new organization",
"teamsAndOrgs.management.organisation.orgCreationNameExistsError": "Organisation name already exists",
"teamsAndOrgs.management.organisation.edit": "Edit organization",
Expand Down
4 changes: 0 additions & 4 deletions frontend/src/views/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,6 @@ export default defineMessages({
id: 'management.stats.users.title',
defaultMessage: 'New users',
},
totalFeatures: {
id: 'management.stats.features',
defaultMessage: 'Total features',
},
newOrganisation: {
id: 'teamsAndOrgs.management.organisation.creation',
defaultMessage: 'Create new organization',
Expand Down
3 changes: 0 additions & 3 deletions frontend/src/views/stats.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,6 @@ export const Stats = () => {
</div>
</div>
<div className="w-100 fl cf mt3 pb3">
<h4 className="f3 fw6 ttu barlow-condensed blue-dark mt0 pt4 mb3">
<FormattedMessage {...messages.totalFeatures} />
</h4>
<FeatureStats />
</div>
</div>
Expand Down

0 comments on commit 0c30aea

Please sign in to comment.