Skip to content

Commit 66b8838

Browse files
authored
Merge pull request #888 from mapswipe/master-rc
Release Footprint validation Changes
2 parents 4675071 + 4a3b5c1 commit 66b8838

File tree

103 files changed

+5624
-1423
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

103 files changed

+5624
-1423
lines changed

api/nginx.conf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ server {
22
listen 80;
33
server_name api;
44

5+
gzip on;
6+
gzip_comp_level 2;
7+
gzip_types text/plain text/csv text/css application/json text/javascript;
8+
59
location / {
610
alias /usr/share/nginx/html/api/;
711
autoindex on;

docker-compose.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@ services:
138138
REACT_APP_SENTRY_DSN: ${MANAGER_DASHBOARD_SENTRY_DSN}
139139
REACT_APP_SENTRY_TRACES_SAMPLE_RATE: ${MANAGER_DASHBOARD_SENTRY_TRACES_SAMPLE_RATE}
140140
REACT_APP_COMMUNITY_DASHBOARD_URL: ${MANAGER_DASHBOARD_COMMUNITY_DASHBOARD_URL}
141+
REACT_APP_IMAGE_BING_API_KEY: ${IMAGE_BING_API_KEY}
142+
REACT_APP_IMAGE_MAPBOX_API_KEY: ${IMAGE_MAPBOX_API_KEY}
143+
REACT_APP_IMAGE_MAXAR_PREMIUM_API_KEY: ${IMAGE_MAXAR_PREMIUM_API_KEY}
144+
REACT_APP_IMAGE_MAXAR_STANDARD_API_KEY: ${IMAGE_MAXAR_STANDARD_API_KEY}
141145
volumes:
142146
- manager-dashboard-static:/code/build/
143147
command: bash -c 'yarn build'

firebase/functions/src/index.ts

Lines changed: 91 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ exports.osmAuth.token = functions.https.onRequest((req, res) => {
3030
3131
This function also writes to the `contributions` section in the user profile.
3232
*/
33-
exports.groupUsersCounter = functions.database.ref('/v2/results/{projectId}/{groupId}/{userId}/').onCreate((snapshot, context) => {
33+
exports.groupUsersCounter = functions.database.ref('/v2/results/{projectId}/{groupId}/{userId}/').onCreate(async (snapshot, context) => {
3434
// these references/values will be updated by this function
3535
const groupUsersRef = admin.database().ref('/v2/groupsUsers/' + context.params.projectId + '/' + context.params.groupId);
3636
const userRef = admin.database().ref('/v2/users/' + context.params.userId);
@@ -53,6 +53,30 @@ exports.groupUsersCounter = functions.database.ref('/v2/results/{projectId}/{gro
5353
}
5454

5555
const result = snapshot.val();
56+
57+
58+
// New versions of app will have the appVersion defined (> 2.2.5)
59+
// appVersion: 2.2.5 (14)-dev
60+
const appVersionString = result.appVersion as string | undefined | null;
61+
62+
// Check if the app is of older version
63+
// (no need to check for specific version since old app won't sent the version info)
64+
if (appVersionString === null || appVersionString === undefined || appVersionString.trim() === '') {
65+
const projectRef = admin.database().ref(`/v2/projects/${context.params.projectId}`);
66+
const dataSnapshot = await projectRef.once('value');
67+
68+
if (dataSnapshot.exists()) {
69+
const project = dataSnapshot.val();
70+
// Check if project type is footprint and also has
71+
// custom options (i.e. these are new type of projects)
72+
if (project.projectType === 2 && project.customOptions) {
73+
// We remove the results submitted from older version of app (< v2.2.6)
74+
console.info(`Result submitted for ${context.params.projectId} was discarded: submitted from older version of app`);
75+
return thisResultRef.remove();
76+
}
77+
}
78+
}
79+
5680
// if result ref does not contain all required attributes we don't updated counters
5781
// e.g. due to some error when uploading from client
5882
if (!Object.prototype.hasOwnProperty.call(result, 'results')) {
@@ -90,77 +114,72 @@ exports.groupUsersCounter = functions.database.ref('/v2/results/{projectId}/{gro
90114
Update overall taskContributionCount and project taskContributionCount in the user profile
91115
based on the number of results submitted and the existing count values.
92116
*/
93-
return groupUsersRef.child(context.params.userId).once('value')
94-
.then((dataSnapshot) => {
95-
if (dataSnapshot.exists()) {
96-
console.log('group contribution exists already. user: '+context.params.userId+' project: '+context.params.projectId+' group: '+context.params.groupId);
97-
return null;
98-
}
99-
const latestNumberOfTasks = Object.keys(result['results']).length;
100-
101-
return Promise.all([
102-
userContributionRef.child(context.params.groupId).set(true),
103-
groupUsersRef.child(context.params.userId).set(true),
104-
totalTaskContributionCountRef.transaction((currentCount) => {
105-
return currentCount + latestNumberOfTasks;
106-
}),
107-
totalGroupContributionCountRef.transaction((currentCount) => {
108-
return currentCount + 1;
109-
}),
110-
taskContributionCountRef.transaction((currentCount) => {
111-
return currentCount + latestNumberOfTasks;
112-
}),
113-
114-
// Tag userGroups of the user in the result
115-
// eslint-disable-next-line promise/no-nesting
116-
userRef.child('userGroups').once('value').then((userGroupsOfTheUserSnapshot) => {
117-
if (!userGroupsOfTheUserSnapshot.exists()) {
118-
return null;
119-
}
120-
121-
// eslint-disable-next-line promise/no-nesting
122-
return userGroupsRef.once('value').then((allUserGroupsSnapshot) => {
123-
if (!allUserGroupsSnapshot.exists()) {
124-
return null;
125-
}
126-
127-
const userGroupsOfTheUserKeyList = Object.keys(userGroupsOfTheUserSnapshot.val());
128-
if (userGroupsOfTheUserKeyList.length <= 0) {
129-
return null;
130-
}
131-
132-
const allUserGroups = allUserGroupsSnapshot.val();
133-
const nonArchivedUserGroupKeys = userGroupsOfTheUserKeyList.filter((key) => {
134-
const currentUserGroup = allUserGroups[key];
135-
136-
// User might have joined some group that was removed but not cleared from their list
137-
if (!currentUserGroup) {
138-
return false;
139-
}
140-
141-
// Skip groups that have been archived
142-
if (currentUserGroup.archivedAt) {
143-
return false;
144-
}
145-
146-
return true;
147-
});
148-
149-
if (nonArchivedUserGroupKeys.length === 0) {
150-
return null;
151-
}
152-
153-
const nonArchivedUserGroupsOfTheUser = nonArchivedUserGroupKeys.reduce((acc, val) => {
154-
acc[val] = true;
155-
return acc;
156-
}, {} as Record<string, boolean>);
157-
158-
// Include userGroups of the user in the results
159-
return thisResultRef.child('userGroups').set(nonArchivedUserGroupsOfTheUser);
160-
});
161-
}),
162-
]);
163-
});
117+
const dataSnapshot = await groupUsersRef.child(context.params.userId).once('value');
118+
if (dataSnapshot.exists()) {
119+
console.log('group contribution exists already. user: '+context.params.userId+' project: '+context.params.projectId+' group: '+context.params.groupId);
120+
return null;
121+
}
122+
123+
const latestNumberOfTasks = Object.keys(result['results']).length;
124+
await Promise.all([
125+
userContributionRef.child(context.params.groupId).set(true),
126+
groupUsersRef.child(context.params.userId).set(true),
127+
totalTaskContributionCountRef.transaction((currentCount) => {
128+
return currentCount + latestNumberOfTasks;
129+
}),
130+
totalGroupContributionCountRef.transaction((currentCount) => {
131+
return currentCount + 1;
132+
}),
133+
taskContributionCountRef.transaction((currentCount) => {
134+
return currentCount + latestNumberOfTasks;
135+
}),
136+
]);
137+
138+
139+
// Tag userGroups of the user in the result
140+
const userGroupsOfTheUserSnapshot = await userRef.child('userGroups').once('value');
141+
if (!userGroupsOfTheUserSnapshot.exists()) {
142+
return null;
143+
}
144+
145+
const allUserGroupsSnapshot = await userGroupsRef.once('value');
146+
if (!allUserGroupsSnapshot.exists()) {
147+
return null;
148+
}
149+
150+
const userGroupsOfTheUserKeyList = Object.keys(userGroupsOfTheUserSnapshot.val());
151+
if (userGroupsOfTheUserKeyList.length <= 0) {
152+
return null;
153+
}
154+
155+
const allUserGroups = allUserGroupsSnapshot.val();
156+
const nonArchivedUserGroupKeys = userGroupsOfTheUserKeyList.filter((key) => {
157+
const currentUserGroup = allUserGroups[key];
158+
159+
// User might have joined some group that was removed but not cleared from their list
160+
if (!currentUserGroup) {
161+
return false;
162+
}
163+
164+
// Skip groups that have been archived
165+
if (currentUserGroup.archivedAt) {
166+
return false;
167+
}
168+
169+
return true;
170+
});
171+
172+
if (nonArchivedUserGroupKeys.length === 0) {
173+
return null;
174+
}
175+
176+
const nonArchivedUserGroupsOfTheUser = nonArchivedUserGroupKeys.reduce((acc, val) => {
177+
acc[val] = true;
178+
return acc;
179+
}, {} as Record<string, boolean>);
180+
181+
// Include userGroups of the user in the results
182+
return thisResultRef.child('userGroups').set(nonArchivedUserGroupsOfTheUser);
164183
});
165184

166185

manager-dashboard/.env.example

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,9 @@ REACT_APP_FIREBASE_STORAGE_BUCKET=
1010
REACT_APP_FIREBASE_MESSAGING_SENDER_ID=
1111
REACT_APP_FIREBASE_APP_ID=
1212
REACT_APP_COMMUNITY_DASHBOARD_URL=
13+
14+
REACT_APP_IMAGE_BING_API_KEY=
15+
REACT_APP_IMAGE_MAPBOX_API_KEY=
16+
REACT_APP_IMAGE_MAXAR_PREMIUM_API_KEY=
17+
# -- NOTE: not used and seems to be discontinued
18+
REACT_APP_IMAGE_MAXAR_STANDARD_API_KEY=

manager-dashboard/app/Base/components/Navbar/styles.css

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
box-shadow: 0 3px 5px -2px var(--color-shadow);
66
background-color: var(--color-primary);
77
padding: var(--spacing-medium) var(--spacing-large);
8-
color: var(--color-text-on-light);
8+
color: var(--color-text-on-dark);
99

1010
.container {
1111
display: flex;
@@ -42,7 +42,7 @@
4242

4343
.link {
4444
opacity: 0.7;
45-
color: var(--color-text-on-light);
45+
color: var(--color-text-on-dark);
4646

4747
&.active {
4848
opacity: 1;
@@ -58,7 +58,7 @@
5858
gap: var(--spacing-medium);
5959

6060
.logout-button {
61-
color: var(--color-text-on-light);
61+
color: var(--color-text-on-dark);
6262
}
6363
}
6464
}

manager-dashboard/app/Base/components/SmartLink/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Link, LinkProps } from 'react-router-dom';
33

44
import {
55
useButtonFeatures,
6-
Props as ButtonProps,
6+
ButtonProps,
77
} from '#components/Button';
88

99
import useRouteMatching, {

manager-dashboard/app/Base/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
ApolloProvider,
1313
} from '@apollo/client';
1414
import { initializeApp } from 'firebase/app';
15+
import 'react-mde/lib/styles/css/react-mde-all.css';
1516

1617
import Init from '#base/components/Init';
1718
import PreloadMessage from '#base/components/PreloadMessage';

manager-dashboard/app/Base/styles.css

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,21 +53,26 @@ p {
5353
--color-background-primary-hint: #0d194920;
5454
--color-background-accent-hint: #4746f610;
5555

56+
--color-black: #000000;
57+
5658
--color-scrollbar-foreground: rgba(0, 0, 0, .2);
5759
--color-scrollbar-background: rgba(0, 0, 0, .01);
5860
--color-hover-background: rgba(0, 0, 0, 0.03);
5961
--color-input-background: #4746f605;
6062

6163
--color-primary: #0d1949;
6264
--color-primary-light: #2d5989;
63-
--color-text-on-light: #ffffff;
65+
--color-text-on-dark: #ffffff;
6466
--color-text-watermark: rgba(0, 0, 0, .4);
6567
--color-text: rgba(0, 0, 0, 0.8);
6668
--color-text-dark: rgba(0, 0, 0, 1);
6769
--color-text-light: rgba(0, 0, 0, 0.6);
70+
--color-text-gray: #666666;
6871
--color-accent: #4746f6;
6972
--color-danger: #e04656;
7073
--color-success: #53b391;
74+
--color-banner-alert: #fdf4dc;
75+
--color-banner-text: #5C4813;
7176
--color-separator: rgba(0, 0, 0, 0.1);
7277
--color-shadow: rgba(0, 0, 0, 0.2);
7378

@@ -83,24 +88,40 @@ p {
8388
--font-size-ultra-large: 4rem;
8489

8590
--font-weight-medium: 400;
91+
--font-weight-semibold: 600;
8692
--font-weight-bold: 700;
8793

8894
--opacity-disabled-element: 0.5;
8995

90-
--width-separator-thin: 1px;
96+
--width-separator-thin: 1pt;
97+
--width-separator-thick: 5pt;
98+
--width-separator-mobile-preview: 8pt;
9199
--width-scrollbar: 0.75rem;
92100
--width-page-content-max: 70rem;
93101
--width-navbar-content-max: 76rem;
94102

103+
--width-mobile-preview: 22rem;
104+
--height-mobile-preview: 40rem;
105+
--height-mobile-preview-builarea-content: 30rem;
106+
--height-mobile-preview-footprint-content: 22rem;
107+
--height-mobile-preview-change-detection-content: 14rem;
108+
95109
--radius-popup-border: 0.25rem;
96110
--radius-scrollbar-border: 0.25rem;
97111
--radius-blur-shadow: 5px;
98112
--radius-modal: 0.25rem;
99113
--radius-button-border: 0.25rem;
100114
--radius-input-border: 0.25rem;
101-
--radius-card-border: 0.25rem;
115+
--radius-card-border: 0.5rem;
102116
--radius-badge-border: 1rem;
103117

118+
--line-height-none: 1;
119+
--line-height-tight: 1.25;
120+
--line-height-snug: 1.375;
121+
--line-height-normal: 1.5;
122+
--line-height-relaxed: 1.625;
123+
--line-height-loose: 2;
124+
104125

105126
--shadow-card: 0 2px 4px -2px var(--color-shadow);
106127

manager-dashboard/app/Base/utils/errorTransform.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { internal } from '@togglecorp/toggle-form';
1+
import { nonFieldError } from '@togglecorp/toggle-form';
22
import { listToMap, isDefined, isNotDefined } from '@togglecorp/fujs';
33

44
interface Error {
5-
[internal]?: string | undefined;
5+
[nonFieldError]?: string | undefined;
66
[key: string]: string | Error | undefined;
77
}
88

@@ -53,7 +53,7 @@ function transformObject(errors: ObjectError[] | undefined): Error | undefined {
5353
);
5454

5555
return {
56-
[internal]: finalNonFieldErrors,
56+
[nonFieldError]: finalNonFieldErrors,
5757
...finalFieldErrors,
5858
};
5959
}
@@ -68,7 +68,7 @@ function transformArray(errors: (ArrayError | null)[] | undefined): Error | unde
6868
const memberErrors = filteredErrors.filter((error) => error.clientId !== 'nonMemberErrors');
6969

7070
return {
71-
[internal]: topLevelError?.messages,
71+
[nonFieldError]: topLevelError?.messages,
7272
...listToMap(
7373
memberErrors,
7474
(error) => error.clientId,
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import React from 'react';
2+
import { IoAlertCircleOutline } from 'react-icons/io5';
3+
import { _cs } from '@togglecorp/fujs';
4+
5+
import Heading from '#components/Heading';
6+
7+
import styles from './styles.css';
8+
9+
interface Props {
10+
className?: string;
11+
title?: React.ReactNode;
12+
children: React.ReactNode;
13+
}
14+
15+
function AlertBanner(props: Props) {
16+
const {
17+
className,
18+
children,
19+
title,
20+
} = props;
21+
22+
return (
23+
<div className={_cs(styles.banner, className)}>
24+
<div className={styles.bannerIcon}>
25+
<IoAlertCircleOutline />
26+
</div>
27+
<div className={styles.container}>
28+
{title && (
29+
<Heading>
30+
{title}
31+
</Heading>
32+
)}
33+
{children}
34+
</div>
35+
</div>
36+
);
37+
}
38+
39+
export default AlertBanner;

0 commit comments

Comments
 (0)