Skip to content

Commit

Permalink
Merge pull request #66 from naxa-developers/enhancement/6241-redesign…
Browse files Browse the repository at this point in the history
…-user-stats

enhancement/6241 redesign user stats
  • Loading branch information
royallsilwallz authored Mar 21, 2024
2 parents bd214d3 + 564154c commit 657ad96
Show file tree
Hide file tree
Showing 10 changed files with 192 additions and 162 deletions.
20 changes: 19 additions & 1 deletion frontend/src/api/stats.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useQuery } from '@tanstack/react-query';
import { fetchExternalJSONAPI } from '../network/genericJSONRequest';

import api from './apiClient';
import { OHSOME_STATS_BASE_URL } from '../config';
Expand Down Expand Up @@ -42,7 +43,7 @@ export const useOsmStatsQuery = () => {
queryKey: ['osm-stats'],
queryFn: fetchOsmStats,
useErrorBoundary: true,
select: (data) => data.data.result
select: (data) => data.data.result,
});
};

Expand All @@ -61,3 +62,20 @@ export const useOsmHashtagStatsQuery = (defaultComment) => {
select: (data) => data.data.result,
});
};

export const useUserOsmStatsQuery = (id) => {
const fetchUserOsmStats = () => {
return fetchExternalJSONAPI(
`${OHSOME_STATS_BASE_URL}/topic/poi,highway,building,waterway/user?userId=${id}`,
true,
);
};

return useQuery({
queryKey: ['user-osm-stats'],
queryFn: fetchUserOsmStats,
useErrorBoundary: true,
select: (data) => data.result,
enabled: !!id,
});
};
14 changes: 14 additions & 0 deletions frontend/src/assets/styles/_extra.scss
Original file line number Diff line number Diff line change
Expand Up @@ -621,3 +621,17 @@ a[href="https://www.mapbox.com/map-feedback/"]
.code {
font-family: inherit;
}

// margin auto
.mt-auto {
margin-top: auto;
}
.mb-auto {
margin-bottom: auto;
}
.ml-auto {
margin-left: auto;
}
.mr-auto {
margin-right: auto;
}
70 changes: 70 additions & 0 deletions frontend/src/components/statsCard.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import { FormattedNumber } from 'react-intl';
import shortNumber from 'short-number';

export const StatsCard = ({ icon, description, value, className, invertColors = false }) => {
return (
Expand All @@ -25,3 +26,72 @@ export const StatsCardContent = ({ value, label, className, invertColors = false
<span className={`ma0 h2 f7 fw5 ${invertColors ? 'white' : 'blue-grey'}`}>{label}</span>
</div>
);

function getFormattedNumber(num) {
if (typeof num !== 'number') return '-';
const value = shortNumber(num);
return typeof value === 'number' ? <FormattedNumber value={Math.abs(value.toFixed(1))} /> : value;
}

export const DetailedStatsCard = ({
icon,
description,
subDescription,
mapped,
created,
modified,
deleted,
unitMore,
unitLess,
}) => {
return (
<div
className="cf pa3 br1 flex bg-white red shadow-6 flex-column justify-between"
style={{ height: '10.5rem', cursor: 'default' }}
>
<div className="flex items-center mb-auto">
<div className="w-25 fl ml2">{icon}</div>
<div>
<h3 className="ma0 mb1 barlow-condensed f2 fw6 red">{getFormattedNumber(mapped)}</h3>
<div className="flex flex-column">
<span className="ma0 f7 fw5 blue-grey mb1">{description}</span>
<span className="ma0 f7 fw5 blue-grey">{subDescription}</span>
</div>
</div>
</div>

{/* seperator line */}
<div className="flex justify-center">
<div className="bg-red mv2" style={{ height: '1px', width: '96%' }} />
</div>

<div className="flex w-100 items-center mt-auto" style={{ justifyContent: 'space-evenly' }}>
<div className="flex flex-column items-center">
<h3 className="ma0 mb2 barlow-condensed fw6 red">{getFormattedNumber(created)}</h3>
<span className="f7 fw5 blue-grey">Created</span>
</div>
<div className="flex flex-column items-center">
<h3 className="ma0 mb2 barlow-condensed fw6 red flex items-center" style={{ gap: '5px' }}>
{unitMore || unitLess ? (
<>
-{getFormattedNumber(unitLess)}
{/* seperator line */}
<div className="flex justify-center">
<div className="bg-blue-grey" style={{ height: '1rem', width: '1px' }} />
</div>
+{getFormattedNumber(unitMore)}
</>
) : (
getFormattedNumber(modified)
)}
</h3>
<span className="f7 fw5 blue-grey">Modified</span>
</div>
<div className="flex flex-column items-center">
<h3 className="ma0 mb2 barlow-condensed fw6 red">{getFormattedNumber(deleted)}</h3>
<span className="f7 fw5 blue-grey">Deleted</span>
</div>
</div>
</div>
);
};
71 changes: 0 additions & 71 deletions frontend/src/components/userDetail/editsByNumbers.js

This file was deleted.

38 changes: 29 additions & 9 deletions frontend/src/components/userDetail/elementsMapped.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
MappedIcon,
ValidatedIcon,
} from '../svgIcons';
import { StatsCard } from '../statsCard';
import { StatsCard, DetailedStatsCard } from '../statsCard';
import StatsTimestamp from '../statsTimestamp';

export const TaskStats = ({ userStats, username }) => {
Expand Down Expand Up @@ -133,25 +133,45 @@ export const ElementsMapped = ({ userStats, osmStats }) => {
description={<FormattedMessage {...messages.timeSpentMapping} />}
value={duration}
/>
<StatsCard
<DetailedStatsCard
icon={<HomeIcon className={iconClass} style={iconStyle} />}
description={<FormattedMessage {...messages.buildingsMapped} />}
value={osmStats.buildings || 0}
subDescription="Created - Deleted"
mapped={osmStats?.building?.value}
created={osmStats?.building?.added}
modified={osmStats?.building?.modified?.count_modified}
deleted={osmStats?.building?.deleted}
/>
<StatsCard
<DetailedStatsCard
icon={<RoadIcon className={iconClass} style={iconStyle} />}
description={<FormattedMessage {...messages.roadMapped} />}
value={osmStats.roads || 0}
subDescription="Created + Modified - Deleted"
mapped={osmStats?.highway?.value}
created={osmStats?.highway?.added}
modified={osmStats?.highway?.modified?.count_modified}
deleted={osmStats?.highway?.deleted}
unitMore={osmStats?.highway?.modified?.unit_more}
unitLess={osmStats?.highway?.modified?.unit_less}
/>
<StatsCard
<DetailedStatsCard
icon={<MarkerIcon className={iconClass} style={iconStyle} />}
description={<FormattedMessage {...messages.poiMapped} />}
value={osmStats.total_poi_count_add || '-'}
subDescription="Created - Deleted"
mapped={osmStats?.poi?.value}
created={osmStats?.poi?.added}
modified={osmStats?.poi?.modified?.count_modified}
deleted={osmStats?.poi?.deleted}
/>
<StatsCard
<DetailedStatsCard
icon={<WavesIcon className={iconClass} style={iconStyle} />}
description={<FormattedMessage {...messages.waterwaysMapped} />}
value={osmStats.total_waterway_km_add || '-'}
subDescription="Created + Modified - Deleted"
mapped={osmStats?.waterway?.value}
created={osmStats?.waterway?.added}
modified={osmStats?.waterway?.modified?.count_modified}
deleted={osmStats?.waterway?.deleted}
unitMore={osmStats?.waterway?.modified?.unit_more}
unitLess={osmStats?.waterway?.modified?.unit_less}
/>
</div>
<div className="cf w-100 relative tr pt3 pr3">
Expand Down
51 changes: 0 additions & 51 deletions frontend/src/components/userDetail/tests/editByNumbers.test.js

This file was deleted.

36 changes: 32 additions & 4 deletions frontend/src/components/userDetail/tests/elementsMapped.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,38 @@ describe('ElementsMapped & TaskStats components', () => {
timeSpentMapping: 3000,
};
const osmStats = {
total_building_count_add: 10,
roads: 229.113,
total_poi_count_add: 15,
total_waterway_count_add: 20,
poi: {
added: 4,
modified: {
count_modified: 1,
},
deleted: 0,
value: 4,
},
highway: {
added: 6,
modified: {
count_modified: 21,
},
deleted: 0,
value: 229,
},
building: {
added: 293,
modified: {
count_modified: 83,
},
deleted: 44,
value: 249,
},
waterway: {
added: 16,
modified: {
count_modified: 27,
},
deleted: 0,
value: 17,
},
};
const { getByText } = render(
<ReduxIntlProviders>
Expand Down
9 changes: 6 additions & 3 deletions frontend/src/views/tests/contributions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,12 @@ describe('Contributions Page Index', () => {
describe('User Stats Page', () => {
it('should render the child component', async () => {
renderWithRouter(
<ReduxIntlProviders>
<UserStats />
</ReduxIntlProviders>,
<QueryClientProviders>
<ReduxIntlProviders>
<UserStats />
</ReduxIntlProviders>
,
</QueryClientProviders>,
);
expect(screen.getByRole('heading', { name: 'Contribution Timeline' })).toBeInTheDocument();
});
Expand Down
Loading

0 comments on commit 657ad96

Please sign in to comment.