diff --git a/README.md b/README.md
index b69e72c3dd..6ff2b82e80 100644
--- a/README.md
+++ b/README.md
@@ -39,7 +39,7 @@ Status | Feature
-
+
## Developers
diff --git a/frontend/public/index.html b/frontend/index.html
similarity index 90%
rename from frontend/public/index.html
rename to frontend/index.html
index 8d5db278e6..2244390f6f 100644
--- a/frontend/public/index.html
+++ b/frontend/index.html
@@ -2,7 +2,7 @@
.env; else cp .env.expand .env; fi\"",
- "start": "npm run preparation && npm run copy-static && npm run patch-id && npm run patch-rapid && craco start",
+ "dev": "vite",
+ "start": "npm run preparation && npm run copy-static && npm run patch-id && npm run patch-rapid && vite start",
"build": "npm run preparation && npm run update-static && npm run patch-id && npm run patch-rapid && craco build && npm run sentry:sourcemaps",
"prettier": "prettier --write 'src/**/*.js'",
"lint": "eslint src",
@@ -117,6 +119,11 @@
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.2.1",
"@testing-library/user-event": "^14.4.3",
+ "@types/chart.js": "^2.9.41",
+ "@types/dompurify": "^3.0.5",
+ "@types/react-test-renderer": "^18.3.0",
+ "@types/slug": "^5.0.9",
+ "@vitejs/plugin-react-swc": "^3.7.0",
"combine-react-intl-messages": "^4.0.0",
"jest-canvas-mock": "^2.5.2",
"msw": "^1.3.2",
@@ -124,7 +131,8 @@
"react-scripts": "^5.0.1",
"react-select-event": "^5.5.1",
"react-test-renderer": "^18.2.0",
- "source-map-explorer": "^2.5.3"
+ "source-map-explorer": "^2.5.3",
+ "vite": "^5.4.2"
},
"resolutions": {
"dom-accessibility-api": "0.5.14"
diff --git a/frontend/src/App.js b/frontend/src/App.tsx
similarity index 97%
rename from frontend/src/App.js
rename to frontend/src/App.tsx
index b79667f555..03a029ade4 100644
--- a/frontend/src/App.js
+++ b/frontend/src/App.tsx
@@ -38,7 +38,7 @@ const queryClient = new QueryClient({
});
const App = () => {
- useMeta({ property: 'og:url', content: process.env.REACT_APP_BASE_URL });
+ useMeta({ property: 'og:url', content: import.meta.env.REACT_APP_BASE_URL });
useMeta({ name: 'author', content: ORG_NAME });
const isLoading = useSelector((state) => state.loader.isLoading);
const locale = useSelector((state) => state.preferences.locale);
diff --git a/frontend/src/api/apiClient.js b/frontend/src/api/apiClient.ts
similarity index 88%
rename from frontend/src/api/apiClient.js
rename to frontend/src/api/apiClient.ts
index ebc860d0f9..f51b172c22 100644
--- a/frontend/src/api/apiClient.js
+++ b/frontend/src/api/apiClient.ts
@@ -2,7 +2,7 @@ import axios from 'axios';
import { API_URL } from '../config';
-const api = (token, locale) => {
+const api = (token?: string, locale?: string) => {
const instance = axios.create({
baseURL: API_URL.toString(),
headers: {
diff --git a/frontend/src/api/notifications.js b/frontend/src/api/notifications.ts
similarity index 69%
rename from frontend/src/api/notifications.js
rename to frontend/src/api/notifications.ts
index 358e66dfc4..f2313dc0c7 100644
--- a/frontend/src/api/notifications.js
+++ b/frontend/src/api/notifications.ts
@@ -4,10 +4,11 @@ import { useQuery } from '@tanstack/react-query';
import { backendToQueryConversion } from '../hooks/UseInboxQueryAPI';
import { remapParamsToAPI } from '../utils/remapParamsToAPI';
import api from './apiClient';
+import { RootStore } from '../store';
-export const useNotificationsQuery = (inboxQuery) => {
- const token = useSelector((state) => state.auth.token);
- const fetchNotifications = async (signal, queryKey) => {
+export const useNotificationsQuery = (inboxQuery: string) => {
+ const token = useSelector((state: RootStore) => state.auth.token);
+ const fetchNotifications = async (signal: AbortSignal, queryKey: string) => {
const [, inboxQuery] = queryKey;
const response = await api(token).get(`notifications/?${serializeParams(inboxQuery)}`, {
signal,
@@ -24,8 +25,8 @@ export const useNotificationsQuery = (inboxQuery) => {
};
export const useUnreadNotificationsCountQuery = () => {
- const token = useSelector((state) => state.auth.token);
- const fetchUnreadNotificationCount = async (signal) => {
+ const token = useSelector((state: RootStore) => state.auth.token);
+ const fetchUnreadNotificationCount = async (signal: AbortSignal) => {
const response = await api(token).get('notifications/queries/own/count-unread/', {
signal,
});
@@ -34,13 +35,15 @@ export const useUnreadNotificationsCountQuery = () => {
return useQuery({
queryKey: ['notifications', 'unread-count'],
- queryFn: ({ signal }) => fetchUnreadNotificationCount(signal),
+ queryFn: ({ signal }: {
+ signal: AbortSignal
+ }) => fetchUnreadNotificationCount(signal),
refetchInterval: 1000 * 30,
refetchOnWindowFocus: true,
});
};
-function serializeParams(queryState) {
+function serializeParams(queryState: unknown) {
const obj = remapParamsToAPI(queryState, backendToQueryConversion);
Object.keys(obj).forEach((key) => {
diff --git a/frontend/src/api/organisations.js b/frontend/src/api/organisations.ts
similarity index 62%
rename from frontend/src/api/organisations.js
rename to frontend/src/api/organisations.ts
index ee539fc308..6a9d383b0f 100644
--- a/frontend/src/api/organisations.js
+++ b/frontend/src/api/organisations.ts
@@ -2,11 +2,14 @@ import { useQuery } from '@tanstack/react-query';
import { useSelector } from 'react-redux';
import api from './apiClient';
+import { RootStore } from '../store';
-export const useUserOrganisationsQuery = (userId) => {
- const token = useSelector((state) => state.auth.token);
+export const useUserOrganisationsQuery = (userId: string | number) => {
+ const token = useSelector((state: RootStore) => state.auth.token);
- const fetchOrganisations = ({ signal }) => {
+ const fetchOrganisations = ({ signal }: {
+ signal: AbortSignal;
+ }) => {
return api(token).get(`organisations/`, {
signal,
params: {
diff --git a/frontend/src/api/projects.js b/frontend/src/api/projects.ts
similarity index 64%
rename from frontend/src/api/projects.js
rename to frontend/src/api/projects.ts
index 770067d272..537f402501 100644
--- a/frontend/src/api/projects.js
+++ b/frontend/src/api/projects.ts
@@ -1,17 +1,18 @@
import axios from 'axios';
import { subMonths, format } from 'date-fns';
-import { useQuery } from '@tanstack/react-query';
+import { QueryKey, QueryOptions, useQuery } from '@tanstack/react-query';
import { useSelector } from 'react-redux';
import { remapParamsToAPI } from '../utils/remapParamsToAPI';
import api from './apiClient';
import { UNDERPASS_URL } from '../config';
+import { RootStore } from '../store';
-export const useProjectsQuery = (fullProjectsQuery, action, queryOptions) => {
- const token = useSelector((state) => state.auth.token);
- const locale = useSelector((state) => state.preferences['locale']);
+export const useProjectsQuery = (fullProjectsQuery: string, action: string, queryOptions: QueryOptions) => {
+ const token = useSelector((state: RootStore) => state.auth.token);
+ const locale = useSelector((state: RootStore) => state.preferences['locale']);
- const fetchProjects = (signal, queryKey) => {
+ const fetchProjects = async (signal: AbortSignal | undefined, queryKey: QueryKey) => {
const [, fullProjectsQuery, action] = queryKey;
const paramsRemapped = remapParamsToAPI(fullProjectsQuery, backendToQueryConversion);
// it's needed in order to query by action when the user goes to /explore page
@@ -23,7 +24,7 @@ export const useProjectsQuery = (fullProjectsQuery, action, queryOptions) => {
paramsRemapped.lastUpdatedTo = format(subMonths(Date.now(), 6), 'yyyy-MM-dd');
}
- return api(token, locale)
+ return await api(token, locale)
.get('projects/', {
signal,
params: paramsRemapped,
@@ -39,10 +40,12 @@ export const useProjectsQuery = (fullProjectsQuery, action, queryOptions) => {
});
};
-export const useProjectQuery = (projectId) => {
- const token = useSelector((state) => state.auth.token);
- const locale = useSelector((state) => state.preferences['locale']);
- const fetchProject = ({ signal }) => {
+export const useProjectQuery = (projectId: string) => {
+ const token = useSelector((state: RootStore) => state.auth.token);
+ const locale = useSelector((state: RootStore) => state.preferences['locale']);
+ const fetchProject = ({ signal }: {
+ signal: AbortSignal;
+ }) => {
return api(token, locale).get(`projects/${projectId}/`, {
signal,
});
@@ -53,11 +56,13 @@ export const useProjectQuery = (projectId) => {
queryFn: fetchProject,
});
};
-export const useProjectSummaryQuery = (projectId, otherOptions = {}) => {
- const token = useSelector((state) => state.auth.token);
- const locale = useSelector((state) => state.preferences['locale']);
+export const useProjectSummaryQuery = (projectId: string, otherOptions = {}) => {
+ const token = useSelector((state: RootStore) => state.auth.token);
+ const locale = useSelector((state: RootStore) => state.preferences['locale']);
- const fetchProjectSummary = ({ signal }) => {
+ const fetchProjectSummary = ({ signal }: {
+ signal: AbortSignal;
+ }) => {
return api(token, locale).get(`projects/${projectId}/queries/summary/`, {
signal,
});
@@ -71,8 +76,10 @@ export const useProjectSummaryQuery = (projectId, otherOptions = {}) => {
});
};
-export const useProjectContributionsQuery = (projectId, otherOptions = {}) => {
- const fetchProjectContributions = ({ signal }) => {
+export const useProjectContributionsQuery = (projectId: string, otherOptions = {}) => {
+ const fetchProjectContributions = ({ signal }: {
+ signal: AbortSignal;
+ }) => {
return api().get(`projects/${projectId}/contributions/`, {
signal,
});
@@ -86,9 +93,11 @@ export const useProjectContributionsQuery = (projectId, otherOptions = {}) => {
});
};
-export const useActivitiesQuery = (projectId) => {
+export const useActivitiesQuery = (projectId: string) => {
const ACTIVITIES_REFETCH_INTERVAL = 1000 * 60;
- const fetchProjectActivities = ({ signal }) => {
+ const fetchProjectActivities = ({ signal }: {
+ signal: AbortSignal;
+ }) => {
return api().get(`projects/${projectId}/activities/latest/`, {
signal,
});
@@ -101,12 +110,14 @@ export const useActivitiesQuery = (projectId) => {
refetchIntervalInBackground: false,
refetchInterval: ACTIVITIES_REFETCH_INTERVAL,
refetchOnWindowFocus: true,
- useErrorBoundary: true,
+ throwOnError: true,
});
};
-export const useTasksQuery = (projectId, otherOptions = {}) => {
- const fetchProjectTasks = ({ signal }) => {
+export const useTasksQuery = (projectId: string, otherOptions = {}) => {
+ const fetchProjectTasks = ({ signal }: {
+ signal: AbortSignal;
+ }) => {
return api().get(`projects/${projectId}/tasks/`, {
signal,
});
@@ -120,8 +131,10 @@ export const useTasksQuery = (projectId, otherOptions = {}) => {
});
};
-export const usePriorityAreasQuery = (projectId) => {
- const fetchProjectPriorityArea = (signal) => {
+export const usePriorityAreasQuery = (projectId: string) => {
+ const fetchProjectPriorityArea = (signal: {
+ signal: AbortSignal;
+ }) => {
return api().get(`projects/${projectId}/queries/priority-areas/`, {
signal,
});
@@ -134,8 +147,10 @@ export const usePriorityAreasQuery = (projectId) => {
});
};
-export const useProjectTimelineQuery = (projectId) => {
- const fetchTimelineData = (signal) => {
+export const useProjectTimelineQuery = (projectId: string) => {
+ const fetchTimelineData = (signal: {
+ signal: AbortSignal;
+ }) => {
return api().get(`projects/${projectId}/contributions/queries/day/`, {
signal,
});
@@ -148,10 +163,12 @@ export const useProjectTimelineQuery = (projectId) => {
});
};
-export const useTaskDetail = (projectId, taskId, shouldRefetch) => {
- const token = useSelector((state) => state.auth.token);
+export const useTaskDetail = (projectId: string, taskId: number, shouldRefetch: boolean) => {
+ const token = useSelector((state: RootStore) => state.auth.token);
- const fetchTaskDetail = ({ signal }) => {
+ const fetchTaskDetail = ({ signal }: {
+ signal: AbortSignal;
+ }) => {
return api(token).get(`projects/${projectId}/tasks/${taskId}/`, {
signal,
});
@@ -167,26 +184,26 @@ export const useTaskDetail = (projectId, taskId, shouldRefetch) => {
};
// MAPPING
-export const stopMapping = (projectId, taskId, comment, token, locale = 'en') => {
+export const stopMapping = (projectId: string, taskId: number, comment: string, token: string, locale: string = 'en') => {
return api(token, locale).post(`projects/${projectId}/tasks/actions/stop-mapping/${taskId}/`, {
comment,
});
};
-export const splitTask = (projectId, taskId, token, locale) => {
+export const splitTask = (projectId: string, taskId: number, token: string, locale: string = 'en') => {
return api(token, locale).post(`projects/${projectId}/tasks/actions/split/${taskId}/`);
};
-export const submitMappingTask = (url, payload, token, locale) => {
+export const submitMappingTask = (url: string, payload: any, token: string, locale: string = 'en') => {
return api(token, locale).post(url, payload);
};
// VALIDATION
-export const stopValidation = (projectId, payload, token, locale = 'en') => {
+export const stopValidation = (projectId: string, payload: any, token: string, locale: string = 'en') => {
return api(token, locale).post(`projects/${projectId}/tasks/actions/stop-validation/`, payload);
};
-export const submitValidationTask = (projectId, payload, token, locale) => {
+export const submitValidationTask = (projectId: string, payload: any, token: string, locale: string = 'en') => {
return api(token, locale).post(
`projects/${projectId}/tasks/actions/unlock-after-validation/`,
payload,
@@ -205,7 +222,7 @@ export const useAvailableCountriesQuery = () => {
});
};
-export const useAllPartnersQuery = (token, userId) => {
+export const useAllPartnersQuery = (token: string, userId: string) => {
const fetchAllPartners = () => {
return api(token).get('partners/');
};
diff --git a/frontend/src/api/questionsAndComments.js b/frontend/src/api/questionsAndComments.ts
similarity index 52%
rename from frontend/src/api/questionsAndComments.js
rename to frontend/src/api/questionsAndComments.ts
index 4c907e7a12..c87b354456 100644
--- a/frontend/src/api/questionsAndComments.js
+++ b/frontend/src/api/questionsAndComments.ts
@@ -2,12 +2,15 @@ import { useQuery } from '@tanstack/react-query';
import { useSelector } from 'react-redux';
import api from './apiClient';
+import { RootStore } from '../store';
-export const useCommentsQuery = (projectId, page) => {
- const token = useSelector((state) => state.auth.token);
- const locale = useSelector((state) => state.preferences['locale']);
+export const useCommentsQuery = (projectId: string, page: number) => {
+ const token = useSelector((state: RootStore) => state.auth.token);
+ const locale = useSelector((state: RootStore) => state.preferences['locale']);
- const getComments = ({ signal }) => {
+ const getComments = ({ signal }: {
+ signal: AbortSignal;
+ }) => {
return api(token, locale).get(`projects/${projectId}/comments/`, {
signal,
params: {
@@ -24,11 +27,11 @@ export const useCommentsQuery = (projectId, page) => {
});
};
-export const postProjectComment = (projectId, comment, token, locale = 'en') => {
+export const postProjectComment = (projectId: string, comment: string, token: string, locale: string = 'en') => {
return api(token, locale).post(`projects/${projectId}/comments/`, { message: comment });
};
-export const postTaskComment = (projectId, taskId, comment, token, locale = 'en') => {
+export const postTaskComment = (projectId: string, taskId: number, comment: string, token: string, locale: string = 'en') => {
return api(token, locale).post(`projects/${projectId}/comments/tasks/${taskId}/`, {
comment,
});
diff --git a/frontend/src/api/stats.js b/frontend/src/api/stats.ts
similarity index 70%
rename from frontend/src/api/stats.js
rename to frontend/src/api/stats.ts
index 1016afac77..59c5701760 100644
--- a/frontend/src/api/stats.js
+++ b/frontend/src/api/stats.ts
@@ -4,13 +4,16 @@ import { fetchExternalJSONAPI } from '../network/genericJSONRequest';
import api from './apiClient';
import { OHSOME_STATS_BASE_URL, defaultChangesetComment } from '../config';
-const ohsomeProxyAPI = (url) => {
+const ohsomeProxyAPI = (url: string) => {
const token = localStorage.getItem('token');
+ if (!token) return null;
return api(token).get(`users/statistics/ohsome/?url=${url}`);
};
export const useSystemStatisticsQuery = () => {
- const fetchSystemStats = ({ signal }) => {
+ const fetchSystemStats = ({ signal }: {
+ signal: AbortSignal;
+ }) => {
return api().get(`system/statistics/`, {
signal,
});
@@ -19,12 +22,14 @@ export const useSystemStatisticsQuery = () => {
return useQuery({
queryKey: ['tm-stats'],
queryFn: fetchSystemStats,
- useErrorBoundary: true,
+ throwOnError: true,
});
};
-export const useProjectStatisticsQuery = (projectId) => {
- const fetchProjectStats = ({ signal }) => {
+export const useProjectStatisticsQuery = (projectId: string) => {
+ const fetchProjectStats = ({ signal }: {
+ signal: AbortSignal;
+ }) => {
return api().get(`projects/${projectId}/statistics/`, {
signal,
});
@@ -38,7 +43,9 @@ export const useProjectStatisticsQuery = (projectId) => {
};
export const useOsmStatsQuery = () => {
- const fetchOsmStats = ({ signal }) => {
+ const fetchOsmStats = ({ signal }: {
+ signal: AbortSignal;
+ }) => {
return api().get(`${OHSOME_STATS_BASE_URL}/stats/${defaultChangesetComment}-%2A`, {
signal,
});
@@ -47,13 +54,15 @@ export const useOsmStatsQuery = () => {
return useQuery({
queryKey: ['osm-stats'],
queryFn: fetchOsmStats,
- useErrorBoundary: true,
+ throwOnError: true,
select: (data) => data.data.result,
});
};
-export const useOsmHashtagStatsQuery = (defaultComment) => {
- const fetchOsmStats = ({ signal }) => {
+export const useOsmHashtagStatsQuery = (defaultComment: string) => {
+ const fetchOsmStats = ({ signal }: {
+ signal: AbortSignal;
+ }) => {
return api().get(`${OHSOME_STATS_BASE_URL}/stats/${defaultComment[0].replace('#', '')}`, {
signal,
});
@@ -62,13 +71,13 @@ export const useOsmHashtagStatsQuery = (defaultComment) => {
return useQuery({
queryKey: ['osm-hashtag-stats'],
queryFn: fetchOsmStats,
- useErrorBoundary: true,
+ throwOnError: true,
enabled: Boolean(defaultComment?.[0]),
select: (data) => data.data.result,
});
};
-export const useUserOsmStatsQuery = (id) => {
+export const useUserOsmStatsQuery = (id: string) => {
const fetchUserOsmStats = () => {
return ohsomeProxyAPI(
`${OHSOME_STATS_BASE_URL}/topic/poi,highway,building,waterway/user?userId=${id}`,
@@ -78,9 +87,9 @@ export const useUserOsmStatsQuery = (id) => {
return useQuery({
queryKey: ['user-osm-stats'],
queryFn: fetchUserOsmStats,
- // userDetail.test.js fails on CI when useErrorBoundary=true
- useErrorBoundary: process.env.NODE_ENV !== 'test',
- select: (data) => data.data.result,
+ // userDetail.test.js fails on CI when throwOnError=true
+ throwOnError: import.meta.env.NODE_ENV !== 'test',
+ select: (data) => data?.data.result,
enabled: !!id,
});
};
@@ -93,7 +102,7 @@ export const useOsmStatsMetadataQuery = () => {
return useQuery({
queryKey: ['osm-stats-metadata'],
queryFn: fetchOsmStatsMetadata,
- useErrorBoundary: true,
+ throwOnError: true,
select: (data) => data.result,
});
};
diff --git a/frontend/src/api/teams.js b/frontend/src/api/teams.ts
similarity index 59%
rename from frontend/src/api/teams.js
rename to frontend/src/api/teams.ts
index c79ae9d76a..e17ee39b7d 100644
--- a/frontend/src/api/teams.js
+++ b/frontend/src/api/teams.ts
@@ -2,11 +2,14 @@ import { useSelector } from 'react-redux';
import { useQuery } from '@tanstack/react-query';
import api from './apiClient';
+import { RootStore } from '../store';
-export const useTeamsQuery = (params, otherOptions) => {
- const token = useSelector((state) => state.auth.token);
+export const useTeamsQuery = (params: any, otherOptions: any) => {
+ const token = useSelector((state: RootStore) => state.auth.token);
- const fetchUserTeams = ({ signal }) => {
+ const fetchUserTeams = ({ signal }: {
+ signal: AbortSignal;
+ }) => {
return api(token).get(`teams/`, {
signal,
params: params,
diff --git a/frontend/src/api/user.js b/frontend/src/api/user.ts
similarity index 58%
rename from frontend/src/api/user.js
rename to frontend/src/api/user.ts
index 641baadfea..e83ffb86b3 100644
--- a/frontend/src/api/user.js
+++ b/frontend/src/api/user.ts
@@ -2,12 +2,15 @@ import { useSelector } from 'react-redux';
import { useQuery } from '@tanstack/react-query';
import api from './apiClient';
+import { RootStore } from '../store';
export const useLockedTasksQuery = () => {
- const token = useSelector((state) => state.auth.token);
- const locale = useSelector((state) => state.preferences['locale']);
+ const token = useSelector((state: RootStore) => state.auth.token);
+ const locale = useSelector((state: RootStore) => state.preferences['locale']);
- const fetchLockedTasks = ({ signal }) => {
+ const fetchLockedTasks = ({ signal }: {
+ signal: AbortSignal;
+ }) => {
return api(token, locale).get(`users/queries/tasks/locked/details/`, {
signal,
});
@@ -17,7 +20,6 @@ export const useLockedTasksQuery = () => {
queryKey: ['locked-tasks'],
queryFn: fetchLockedTasks,
select: (data) => data.data?.tasks,
- cacheTime: 0,
- useErrorBoundary: true,
+ throwOnError: true,
});
};
diff --git a/frontend/src/components/alert/index.js b/frontend/src/components/alert/index.tsx
similarity index 78%
rename from frontend/src/components/alert/index.js
rename to frontend/src/components/alert/index.tsx
index 7539c03746..28facb7820 100644
--- a/frontend/src/components/alert/index.js
+++ b/frontend/src/components/alert/index.tsx
@@ -9,6 +9,12 @@ export const Alert = ({
inline = false,
iconClassName,
children,
+}: {
+ type: 'info' | 'success' | 'warning' | 'error';
+ compact?: boolean;
+ inline?: boolean;
+ iconClassName?: string;
+ children: React.ReactNode;
}) => {
const icons = {
info: InfoIcon,
@@ -33,9 +39,8 @@ export const Alert = ({
return (
{children}
@@ -43,7 +48,10 @@ export const Alert = ({
);
};
-export function EntityError({ entity, action = 'creation' }) {
+export function EntityError({ entity, action = 'creation' }: {
+ entity: string;
+ action?: 'creation' | 'updation';
+}) {
const messageType = action === 'updation' ? 'entityInfoUpdationFailure' : 'entityCreationFailure';
return (
diff --git a/frontend/src/components/alert/tests/index.test.js b/frontend/src/components/alert/tests/index.test.tsx
similarity index 89%
rename from frontend/src/components/alert/tests/index.test.js
rename to frontend/src/components/alert/tests/index.test.tsx
index 3b28c1c35d..b1a281ec9b 100644
--- a/frontend/src/components/alert/tests/index.test.js
+++ b/frontend/src/components/alert/tests/index.test.tsx
@@ -7,7 +7,7 @@ describe('Alert Component', () => {
const { container } = render(
An error message );
expect(container.querySelector('svg')).toBeInTheDocument(); // ban icon
expect(container.querySelector('.dark-red')).toBeInTheDocument();
- expect(container.querySelector('div').className).toBe(
+ expect(container.querySelector('div')?.className).toBe(
'db blue-dark bl bw2 br2 pa3 b--dark-red bg-washed-red',
);
expect(screen.queryByText(/An error message/)).toBeInTheDocument();
@@ -21,7 +21,7 @@ describe('Alert Component', () => {
);
expect(container.querySelector('svg')).toBeInTheDocument();
expect(container.querySelector('.dark-green')).toBeInTheDocument();
- const divClassName = container.querySelector('div').className;
+ const divClassName = container.querySelector('div')?.className;
expect(divClassName).toContain('b--dark-green bg-washed-green');
expect(divClassName).toContain('di');
expect(divClassName).not.toContain('db');
@@ -36,7 +36,7 @@ describe('Alert Component', () => {
);
expect(container.querySelector('svg')).toBeInTheDocument();
expect(container.querySelector('.blue')).toBeInTheDocument();
- const divClassName = container.querySelector('div').className;
+ const divClassName = container.querySelector('div')?.className;
expect(divClassName).toContain('b--blue bg-lightest-blue');
expect(divClassName).toContain('db');
expect(divClassName).not.toContain('di');
@@ -53,7 +53,7 @@ describe('Alert Component', () => {
);
expect(container.querySelector('svg')).toBeInTheDocument();
expect(container.querySelector('.gold')).toBeInTheDocument();
- const divClassName = container.querySelector('div').className;
+ const divClassName = container.querySelector('div')?.className;
expect(divClassName).toContain('b--gold bg-washed-yellow');
expect(divClassName).toContain('di');
expect(divClassName).toContain('pa2');
diff --git a/frontend/src/components/banner/index.js b/frontend/src/components/banner/index.tsx
similarity index 100%
rename from frontend/src/components/banner/index.js
rename to frontend/src/components/banner/index.tsx
diff --git a/frontend/src/components/banner/messages.js b/frontend/src/components/banner/messages.ts
similarity index 100%
rename from frontend/src/components/banner/messages.js
rename to frontend/src/components/banner/messages.ts
diff --git a/frontend/src/components/banner/tests/topBanner.test.js b/frontend/src/components/banner/tests/topBanner.test.tsx
similarity index 100%
rename from frontend/src/components/banner/tests/topBanner.test.js
rename to frontend/src/components/banner/tests/topBanner.test.tsx
diff --git a/frontend/src/components/banner/topBanner.js b/frontend/src/components/banner/topBanner.tsx
similarity index 57%
rename from frontend/src/components/banner/topBanner.js
rename to frontend/src/components/banner/topBanner.tsx
index 7cc1bcf895..cf65aee73f 100644
--- a/frontend/src/components/banner/topBanner.js
+++ b/frontend/src/components/banner/topBanner.tsx
@@ -2,18 +2,30 @@ import { useLocation } from 'react-router-dom';
import { useFetchWithAbort } from '../../hooks/UseFetch';
import { htmlFromMarkdown } from '../../utils/htmlFromMarkdown';
import './styles.scss';
+import { useEffect, useState } from 'react';
export function TopBanner() {
const location = useLocation();
const [, error, data] = useFetchWithAbort(`system/banner/`);
+ const [dangerousInner, setDangerousInner] = useState
(null);
+
+ useEffect(() => {
+ (async () => {
+ // @ts-expect-error
+ setDangerousInner((await htmlFromMarkdown(data?.message)).__html)
+ })
+ }, [data])
return (
<>
+ {/* @ts-expect-error TS Migrations */}
{location.pathname === '/' && data.visible && !error && (
)}
diff --git a/frontend/src/components/button.js b/frontend/src/components/button.tsx
similarity index 54%
rename from frontend/src/components/button.js
rename to frontend/src/components/button.tsx
index af665a7a92..cd429e5ace 100644
--- a/frontend/src/components/button.js
+++ b/frontend/src/components/button.tsx
@@ -3,22 +3,31 @@ import { Link } from 'react-router-dom';
import { LoadingIcon } from './svgIcons';
import React from 'react';
-const IconSpace = ({ children }) => {children} ;
+const IconSpace = ({ children }: {
+ children: React.ReactNode,
+}) => {children} ;
export const AnimatedLoadingIcon = () => (
);
-export function Button({ onClick, children, icon, className, disabled, loading = false }: Object) {
+export type ButtonProps = React.ButtonHTMLAttributes & {
+ icon?: React.ReactNode,
+ loading?: boolean,
+};
+
+export function Button(props: ButtonProps) {
+ const { children, icon, className, loading = false, disabled, ...rest } = props
+
return (
{loading ? : icon && {icon} }
{children}
@@ -26,24 +35,22 @@ export function Button({ onClick, children, icon, className, disabled, loading =
);
}
-export function FormSubmitButton({
- children,
- className,
- icon,
- disabledClassName,
- disabled,
- loading = false,
-}: Object) {
+export function FormSubmitButton(props: React.ButtonHTMLAttributes & {
+ icon?: React.ReactNode,
+ loading?: boolean,
+ disabledClassName?: string,
+}) {
+ const { children, icon, className, loading = false, disabled, disabledClassName, ...rest } = props
return (
{loading ? : icon && {icon} }
{children}
@@ -51,16 +58,21 @@ export function FormSubmitButton({
);
}
-export const CustomButton = React.forwardRef(
- ({ onClick, children, icon, className, disabled, loading = false }, ref): Object => {
+export const CustomButton = React.forwardRef & {
+ icon?: React.ReactNode,
+ loading?: boolean,
+}>(
+ (props, ref) => {
+ const { children, icon, className, loading = false, disabled, onClick, ...rest } = props;
return (
{loading ? : icon && {icon} }
{children}
@@ -69,10 +81,14 @@ export const CustomButton = React.forwardRef(
},
);
-export function EditButton({ url, children, className = 'mh1 mv1' }: Object) {
+export function EditButton(props: React.AnchorHTMLAttributes & {
+ url: string,
+}) {
+ const { children, className = "mh1 mv1", url, ...rest } = props;
return (
{children}
diff --git a/frontend/src/components/comments/commentInput.js b/frontend/src/components/comments/commentInput.tsx
similarity index 88%
rename from frontend/src/components/comments/commentInput.js
rename to frontend/src/components/comments/commentInput.tsx
index 96be48c554..27fd752a23 100644
--- a/frontend/src/components/comments/commentInput.js
+++ b/frontend/src/components/comments/commentInput.tsx
@@ -17,6 +17,7 @@ import { htmlFromMarkdown, formatUserNamesToLink } from '../../utils/htmlFromMar
import { iconConfig } from './editorIconConfig';
import messages from './messages';
import { CurrentUserAvatar } from '../user/avatar';
+import { RootStore } from '../../store';
function CommentInputField({
comment,
@@ -29,16 +30,27 @@ function CommentInputField({
isShowUserPicture = false,
placeholderMsg = messages.leaveAComment,
markdownTextareaProps = {},
-}: Object) {
- const token = useSelector((state) => state.auth.token);
+}: {
+ comment: string;
+ setComment: (comment: string) => void;
+ contributors: string[] | undefined;
+ enableHashtagPaste?: boolean;
+ isShowTabNavs?: boolean;
+ isShowFooter?: boolean;
+ enableContributorsHashtag?: boolean;
+ isShowUserPicture?: boolean;
+ placeholderMsg?: any;
+ markdownTextareaProps?: any;
+}) {
+ const token = useSelector((state: RootStore) => state.auth.token);
const textareaRef = useRef();
const isBundle = useRef(false);
const [isShowPreview, setIsShowPreview] = useState(false);
- const appendImgToComment = (url) => setComment(`${comment}\n![image](${url})\n`);
+ const appendImgToComment = (url: string) => setComment(`${comment}\n![image](${url})\n`);
const [uploadError, uploading, onDrop] = useOnDrop(appendImgToComment);
const { fileRejections, getRootProps, getInputProps } = useDropzone({
- onDrop,
+ onDrop: onDrop ?? undefined,
...DROPZONE_SETTINGS,
});
const [fileuploadError, fileuploading, uploadImg] = useUploadImage();
@@ -95,18 +107,16 @@ function CommentInputField({
setIsShowPreview(false)}
>
setIsShowPreview(true)}
>
diff --git a/frontend/src/components/comments/editorIconConfig.js b/frontend/src/components/comments/editorIconConfig.tsx
similarity index 97%
rename from frontend/src/components/comments/editorIconConfig.js
rename to frontend/src/components/comments/editorIconConfig.tsx
index 7b3178a89a..9b5ae6d82a 100644
--- a/frontend/src/components/comments/editorIconConfig.js
+++ b/frontend/src/components/comments/editorIconConfig.tsx
@@ -6,6 +6,8 @@ import {
unorderedListCommand,
selectWord,
orderedListCommand,
+ ExecuteState,
+ TextAreaTextApi,
} from '@uiw/react-md-editor';
const ICON_SIZE = 14;
@@ -103,7 +105,8 @@ export const iconConfig = {
),
- execute: (state, api) => {
+ execute: (state: ExecuteState, api: TextAreaTextApi) => {
+ // @ts-expect-error TS Migrations - we need to look at what prefix does - can't find it in the docs'
const newSelectionRange = selectWord({ text: state.text, selection: state.selection });
const state1 = api.setSelectionRange(newSelectionRange);
const state2 = api.replaceSelection(`@${state1.selectedText}`);
@@ -131,7 +134,7 @@ export const iconConfig = {
),
execute: () => {
- document.getElementById('image_picker').click();
+ document.getElementById('image_picker')?.click();
},
},
// The backend converts markdown into HTML, so youtube embed iframe works on the preview mode,
diff --git a/frontend/src/components/comments/fileRejections.js b/frontend/src/components/comments/fileRejections.tsx
similarity index 62%
rename from frontend/src/components/comments/fileRejections.js
rename to frontend/src/components/comments/fileRejections.tsx
index 005541cc22..6ddbefecdb 100644
--- a/frontend/src/components/comments/fileRejections.js
+++ b/frontend/src/components/comments/fileRejections.tsx
@@ -1,10 +1,14 @@
-const FileRejections = ({ files }: Object) => {
+import { FileRejection } from "react-dropzone";
+
+const FileRejections = ({ files }: {
+ files: FileRejection[]
+}) => {
// Component that receives the rejected files from Dropzone
return (
{files.map(({ file, errors }) => (
-
- {file.path} (
+
+ {file.name} (
{errors.map((e) => (
{e.message},
diff --git a/frontend/src/components/comments/hashtagPaste.js b/frontend/src/components/comments/hashtagPaste.tsx
similarity index 77%
rename from frontend/src/components/comments/hashtagPaste.js
rename to frontend/src/components/comments/hashtagPaste.tsx
index 63eef64fc3..87e16c23c2 100644
--- a/frontend/src/components/comments/hashtagPaste.js
+++ b/frontend/src/components/comments/hashtagPaste.tsx
@@ -2,13 +2,19 @@ import { useIntl } from 'react-intl';
import messages from './messages';
-export default function HashtagPaste({ text, setFn, hashtag, className }: Object) {
+export default function HashtagPaste({ text, setFn, hashtag, className }: {
+ text: string;
+ setFn: (text: string) => void;
+ hashtag: string;
+ className: string;
+}) {
const intl = useIntl();
return (
setFn(text ? `${text} ${hashtag}` : `${hashtag} `)}
+ // @ts-expect-error TS Migrations
title={intl.formatMessage(messages[`${hashtag.replace('#', '')}HashtagTip`], {
hashtag: hashtag,
})}
diff --git a/frontend/src/components/comments/messages.js b/frontend/src/components/comments/messages.ts
similarity index 100%
rename from frontend/src/components/comments/messages.js
rename to frontend/src/components/comments/messages.ts
diff --git a/frontend/src/components/comments/status.js b/frontend/src/components/comments/status.tsx
similarity index 81%
rename from frontend/src/components/comments/status.js
rename to frontend/src/components/comments/status.tsx
index 1a2987c211..446874adf6 100644
--- a/frontend/src/components/comments/status.js
+++ b/frontend/src/components/comments/status.tsx
@@ -3,7 +3,10 @@ import { FormattedMessage } from 'react-intl';
import messages from './messages';
import { Alert } from '../alert';
-export const MessageStatus = ({ status, comment }) => {
+export const MessageStatus = ({ status, comment }: {
+ status: 'success' | 'error',
+ comment?: string,
+}) => {
if (status === 'success' && !comment) {
return (
diff --git a/frontend/src/components/comments/tests/fileRejections.test.js b/frontend/src/components/comments/tests/fileRejections.test.tsx
similarity index 77%
rename from frontend/src/components/comments/tests/fileRejections.test.js
rename to frontend/src/components/comments/tests/fileRejections.test.tsx
index 0391f7cba8..8fe9e1771c 100644
--- a/frontend/src/components/comments/tests/fileRejections.test.js
+++ b/frontend/src/components/comments/tests/fileRejections.test.tsx
@@ -6,7 +6,7 @@ import FileRejections from '../fileRejections';
describe('FileRejections', () => {
it('with an empty file array renders only a ul element', () => {
const { container } = render( );
- expect(container.querySelector('ul').children.length).toBe(0);
+ expect(container.querySelector('ul')?.children.length).toBe(0);
});
it('renders a li element to each file', () => {
@@ -23,13 +23,14 @@ describe('FileRejections', () => {
errors: [{ code: 1, message: 'Format not supported' }],
},
];
+ // @ts-expect-error TS Migrations
const { container } = render( );
expect(container.querySelectorAll('li').length).toBe(2);
expect(container.querySelectorAll('li')[0].className).toBe('red');
- expect(screen.queryByText(/file.csv/).className).toBe('red');
- expect(screen.queryByText(/file.txt/).className).toBe('red');
+ expect(screen.queryByText(/file.csv/)?.className).toBe('red');
+ expect(screen.queryByText(/file.txt/)?.className).toBe('red');
expect(screen.queryAllByText(/Format not supported/).length).toBe(2);
expect(screen.queryAllByText(/Format not supported/)[0].className).toBe('dib pr2');
- expect(screen.queryByText(/Size bigger than 100kb/).className).toBe('dib pr2');
+ expect(screen.queryByText(/Size bigger than 100kb/)?.className).toBe('dib pr2');
});
});
diff --git a/frontend/src/components/comments/tests/hashtagPaste.test.js b/frontend/src/components/comments/tests/hashtagPaste.test.tsx
similarity index 100%
rename from frontend/src/components/comments/tests/hashtagPaste.test.js
rename to frontend/src/components/comments/tests/hashtagPaste.test.tsx
diff --git a/frontend/src/components/comments/tests/status.test.js b/frontend/src/components/comments/tests/status.test.tsx
similarity index 100%
rename from frontend/src/components/comments/tests/status.test.js
rename to frontend/src/components/comments/tests/status.test.tsx
diff --git a/frontend/src/components/comments/tests/uploadStatus.test.js b/frontend/src/components/comments/tests/uploadStatus.test.tsx
similarity index 91%
rename from frontend/src/components/comments/tests/uploadStatus.test.js
rename to frontend/src/components/comments/tests/uploadStatus.test.tsx
index a86ad04a04..9e08084bd0 100644
--- a/frontend/src/components/comments/tests/uploadStatus.test.js
+++ b/frontend/src/components/comments/tests/uploadStatus.test.tsx
@@ -39,7 +39,7 @@ describe('DropzoneUploadStatus when', () => {
,
);
expect(screen.queryByText('Uploading file...')).toBeInTheDocument();
- expect(screen.queryByText('Uploading file...').className).toBe('blue-grey f6 pt3 db');
+ expect(screen.queryByText('Uploading file...')?.className).toBe('blue-grey f6 pt3 db');
expect(screen.queryByText('The image upload failed.')).not.toBeInTheDocument();
});
it('uploading is false and uploadError is true', () => {
@@ -50,6 +50,6 @@ describe('DropzoneUploadStatus when', () => {
);
expect(screen.queryByText('Uploading file...')).not.toBeInTheDocument();
expect(screen.queryByText('The image upload failed.')).toBeInTheDocument();
- expect(screen.queryByText('The image upload failed.').className).toBe('red f6 pt3 db');
+ expect(screen.queryByText('The image upload failed.')?.className).toBe('red f6 pt3 db');
});
});
diff --git a/frontend/src/components/comments/uploadStatus.js b/frontend/src/components/comments/uploadStatus.tsx
similarity index 78%
rename from frontend/src/components/comments/uploadStatus.js
rename to frontend/src/components/comments/uploadStatus.tsx
index a6c5448463..74eeb3e758 100644
--- a/frontend/src/components/comments/uploadStatus.js
+++ b/frontend/src/components/comments/uploadStatus.tsx
@@ -2,7 +2,10 @@ import { FormattedMessage } from 'react-intl';
import messages from './messages';
-const DropzoneUploadStatus = ({ uploadError, uploading }: Object) => {
+const DropzoneUploadStatus = ({ uploadError, uploading }: {
+ uploadError?: boolean | null;
+ uploading?: boolean | null;
+}) => {
return (
<>
{uploadError && (
diff --git a/frontend/src/components/contributions/messages.js b/frontend/src/components/contributions/messages.ts
similarity index 100%
rename from frontend/src/components/contributions/messages.js
rename to frontend/src/components/contributions/messages.ts
diff --git a/frontend/src/components/contributions/myProjectsDropdown.js b/frontend/src/components/contributions/myProjectsDropdown.tsx
similarity index 70%
rename from frontend/src/components/contributions/myProjectsDropdown.js
rename to frontend/src/components/contributions/myProjectsDropdown.tsx
index 36c9afd24e..59aff92b35 100644
--- a/frontend/src/components/contributions/myProjectsDropdown.js
+++ b/frontend/src/components/contributions/myProjectsDropdown.tsx
@@ -3,12 +3,17 @@ import Select from 'react-select';
import { FormattedMessage } from 'react-intl';
import { useFetch } from '../../hooks/UseFetch';
import messages from './messages';
+import { RootStore } from '../../store';
-export default function MyProjectsDropdown({ className, setQuery, allQueryParams }) {
- const username = useSelector((state) => state.auth.userDetails.username);
+export default function MyProjectsDropdown({ className, setQuery, allQueryParams }: {
+ className?: string;
+ setQuery: any;
+ allQueryParams: any;
+}) {
+ const username = useSelector((state: RootStore) => state.auth.userDetails?.username);
const [, , projects] = useFetch(`projects/queries/${username}/touched/`);
- const onSortSelect = (projectId) => {
+ const onSortSelect = (projectId: string) => {
setQuery(
{
...allQueryParams,
@@ -19,7 +24,10 @@ export default function MyProjectsDropdown({ className, setQuery, allQueryParams
);
};
- const options = projects.mappedProjects?.map(({ projectId }) => ({
+ // @ts-expect-error TS Migrations
+ const options = projects.mappedProjects?.map(({ projectId }: {
+ projectId: string;
+ }) => ({
label: projectId,
value: projectId,
}));
@@ -33,6 +41,7 @@ export default function MyProjectsDropdown({ className, setQuery, allQueryParams
onChange={(e) => onSortSelect(e.value)}
options={options}
placeholder={ }
+ // @ts-expect-error TS Migrations
value={options?.find(({ value }) => value === allQueryParams.projectId) || null}
/>
diff --git a/frontend/src/components/contributions/myTasksNav.js b/frontend/src/components/contributions/myTasksNav.tsx
similarity index 100%
rename from frontend/src/components/contributions/myTasksNav.js
rename to frontend/src/components/contributions/myTasksNav.tsx
diff --git a/frontend/src/components/contributions/myTasksOrderDropdown.js b/frontend/src/components/contributions/myTasksOrderDropdown.tsx
similarity index 75%
rename from frontend/src/components/contributions/myTasksOrderDropdown.js
rename to frontend/src/components/contributions/myTasksOrderDropdown.tsx
index 4e0db9f38f..aa8d6372e9 100644
--- a/frontend/src/components/contributions/myTasksOrderDropdown.js
+++ b/frontend/src/components/contributions/myTasksOrderDropdown.tsx
@@ -2,7 +2,11 @@ import { FormattedMessage } from 'react-intl';
import { Dropdown } from '../dropdown';
import messages from './messages';
-export default function MyTasksOrderDropdown({ className, setQuery, allQueryParams }) {
+export default function MyTasksOrderDropdown({ className, setQuery, allQueryParams }: {
+ className?: string;
+ setQuery: Function;
+ allQueryParams: unknown;
+}) {
const options = [
{
label: ,
@@ -14,11 +18,13 @@ export default function MyTasksOrderDropdown({ className, setQuery, allQueryPara
},
];
- const onSortSelect = (arr) =>
+ const onSortSelect = (arr: unknown[]) =>
setQuery(
{
+ // @ts-expect-error TS Migrations
...allQueryParams,
page: undefined,
+ // @ts-expect-error TS Migrations
orderBy: arr[0].value,
},
'pushIn',
@@ -28,6 +34,7 @@ export default function MyTasksOrderDropdown({ className, setQuery, allQueryPara
}
className={`ba b--grey-light bg-white mr1 v-mid pv2 ${className || ''}`}
diff --git a/frontend/src/components/contributions/taskCard.js b/frontend/src/components/contributions/taskCard.tsx
similarity index 100%
rename from frontend/src/components/contributions/taskCard.js
rename to frontend/src/components/contributions/taskCard.tsx
diff --git a/frontend/src/components/contributions/taskResults.js b/frontend/src/components/contributions/taskResults.tsx
similarity index 69%
rename from frontend/src/components/contributions/taskResults.js
rename to frontend/src/components/contributions/taskResults.tsx
index 8c6aa61362..cdd045d445 100644
--- a/frontend/src/components/contributions/taskResults.js
+++ b/frontend/src/components/contributions/taskResults.tsx
@@ -5,7 +5,25 @@ import 'react-placeholder/lib/reactPlaceholder.css';
import messages from './messages';
import { TaskCard } from './taskCard';
-export const TaskResults = (props) => {
+export interface TaskCardProps {
+ projectId: number;
+ taskId: number;
+}
+
+interface TaskResultsProps {
+ state: {
+ isLoading: boolean;
+ isError: boolean;
+ tasks: TaskCardProps[];
+ pagination?: {
+ total: number;
+ };
+ };
+ className?: string;
+ retryFn: () => void;
+}
+
+export const TaskResults: React.FC = (props) => {
const state = props.state;
return (
@@ -15,8 +33,8 @@ export const TaskResults = (props) => {
,
+ number: state.tasks?.length ?? 0,
+ total: ,
}}
/>
@@ -40,7 +58,11 @@ export const TaskResults = (props) => {
);
};
-export const TaskCards = ({ pageOfCards }) => {
+interface TaskCardsProps {
+ pageOfCards?: TaskCardProps[];
+}
+
+export const TaskCards: React.FC = ({ pageOfCards }) => {
if (pageOfCards?.length === 0) {
return (
diff --git a/frontend/src/components/contributions/tests/myProjectsDropdown.test.js b/frontend/src/components/contributions/tests/myProjectsDropdown.test.tsx
similarity index 100%
rename from frontend/src/components/contributions/tests/myProjectsDropdown.test.js
rename to frontend/src/components/contributions/tests/myProjectsDropdown.test.tsx
diff --git a/frontend/src/components/contributions/tests/myTasksNav.test.js b/frontend/src/components/contributions/tests/myTasksNav.test.tsx
similarity index 100%
rename from frontend/src/components/contributions/tests/myTasksNav.test.js
rename to frontend/src/components/contributions/tests/myTasksNav.test.tsx
diff --git a/frontend/src/components/contributions/tests/myTasksOrderDropdown.test.js b/frontend/src/components/contributions/tests/myTasksOrderDropdown.test.tsx
similarity index 98%
rename from frontend/src/components/contributions/tests/myTasksOrderDropdown.test.js
rename to frontend/src/components/contributions/tests/myTasksOrderDropdown.test.tsx
index f3b3556f23..85a8a12223 100644
--- a/frontend/src/components/contributions/tests/myTasksOrderDropdown.test.js
+++ b/frontend/src/components/contributions/tests/myTasksOrderDropdown.test.tsx
@@ -49,6 +49,7 @@ describe('MyTasksOrderDropdown', () => {
allQueryParams={{
orderBy: '-project_id',
}}
+ setQuery={setQueryMock}
/>
,
);
diff --git a/frontend/src/components/deleteModal/index.js b/frontend/src/components/deleteModal/index.js
index cd36fefea9..4161876ad0 100644
--- a/frontend/src/components/deleteModal/index.js
+++ b/frontend/src/components/deleteModal/index.js
@@ -7,12 +7,12 @@ import Popup from 'reactjs-popup';
import messages from './messages';
import { fetchLocalJSONAPI } from '../../network/genericJSONRequest';
import { DeleteButton } from '../teamsAndOrgs/management';
-import { Button } from '../button';
+import { Button } from '../button.jsx';
import { AlertIcon } from '../svgIcons';
const DeleteTrigger = forwardRef((props, ref) =>
);
-export function DeleteModal({ id, name, type, className, endpointURL, onDelete }: Object) {
+export function DeleteModal({ id, name, type, className, endpointURL, onDelete }) {
const navigate = useNavigate();
const modalRef = useRef();
const token = useSelector((state) => state.auth.token);
diff --git a/frontend/src/components/deleteModal/messages.js b/frontend/src/components/deleteModal/messages.ts
similarity index 100%
rename from frontend/src/components/deleteModal/messages.js
rename to frontend/src/components/deleteModal/messages.ts
diff --git a/frontend/src/components/deleteModal/tests/index.test.js b/frontend/src/components/deleteModal/tests/index.test.tsx
similarity index 100%
rename from frontend/src/components/deleteModal/tests/index.test.js
rename to frontend/src/components/deleteModal/tests/index.test.tsx
diff --git a/frontend/src/components/dropdown.js b/frontend/src/components/dropdown.tsx
similarity index 58%
rename from frontend/src/components/dropdown.js
rename to frontend/src/components/dropdown.tsx
index 45a4c61fe5..79230365bd 100644
--- a/frontend/src/components/dropdown.js
+++ b/frontend/src/components/dropdown.tsx
@@ -1,15 +1,37 @@
-import { createRef, forwardRef, useEffect, useState } from 'react';
+import { createRef, forwardRef, useEffect, useState, RefObject } from 'react';
import { useNavigate } from 'react-router-dom';
import { ChevronDownIcon, CheckIcon } from './svgIcons';
import { CustomButton } from './button';
-const DropdownContent = forwardRef((props, ref) => {
+interface DropdownOption {
+ label: string;
+ value: string;
+ href?: string;
+ internalLink?: boolean;
+}
+
+interface DropdownContentProps {
+ value: DropdownOption | DropdownOption[];
+ onChange?: (value: DropdownOption[]) => void;
+ onRemove?: (option: DropdownOption) => void;
+ onAdd?: (option: DropdownOption) => void;
+ toggleDropdown: () => void;
+ multi?: boolean;
+ toTop?: boolean;
+ options: DropdownOption[];
+ deletable?: (value: string) => void;
+}
+
+const DropdownContent = forwardRef
((props, ref) => {
const navigate = useNavigate();
- const isActive = (obj) => {
- return props.value === obj.value;
+
+ const isActive = (obj: DropdownOption): boolean => {
+ return Array.isArray(props.value)
+ ? props.value.some(item => item.value === obj.value)
+ : props.value.value === obj.value;
};
- const handleClick = (data) => {
+ const handleClick = (data: DropdownOption) => {
if (data) {
const label = data.label;
if (!props.value || !props.onChange) {
@@ -24,22 +46,24 @@ const DropdownContent = forwardRef((props, ref) => {
if (!ourObj) return;
let isRemove = false;
- for (let x = 0; x < value.length; x++) {
- if (value[x].label === label) {
- isRemove = true;
- props.onRemove && props.onRemove(ourObj);
- props.onChange(value.slice(0, x).concat(value.slice(x + 1)));
+ if (Array.isArray(value)) {
+ for (let x = 0; x < value.length; x++) {
+ if (value[x].label === label) {
+ isRemove = true;
+ props.onRemove && props.onRemove(ourObj);
+ props.onChange(value.slice(0, x).concat(value.slice(x + 1)));
+ }
}
- }
- if (!isRemove) {
- let newArray = value.slice(0, value.length);
- if (!props.multi) {
- newArray = [];
+ if (!isRemove) {
+ let newArray = value.slice(0, value.length);
+ if (!props.multi) {
+ newArray = [];
+ }
+ newArray.push(ourObj);
+ props.onAdd && props.onAdd(ourObj);
+ props.onChange(newArray);
}
- newArray.push(ourObj);
- props.onAdd && props.onAdd(ourObj);
- props.onChange(newArray);
}
}
if (!props.multi) {
@@ -104,7 +128,7 @@ const DropdownContent = forwardRef((props, ref) => {
e.preventDefault();
e.stopPropagation();
props.toggleDropdown();
- props.deletable(i.value);
+ props.deletable?.(i.value);
}}
>
x
@@ -116,19 +140,32 @@ const DropdownContent = forwardRef((props, ref) => {
);
});
-export function Dropdown(props) {
- const [display, setDisplay] = useState(false);
+interface DropdownProps {
+ options: DropdownOption[];
+ value: DropdownOption | DropdownOption[]; // Changed here
+ display: string;
+ className?: string;
+ toTop?: boolean;
+ multi?: boolean;
+ onChange?: (value: DropdownOption[]) => void;
+ onRemove?: (option: DropdownOption) => void;
+ onAdd?: (option: DropdownOption) => void;
+ deletable?: (value: string) => void;
+}
+
+export function Dropdown(props: DropdownProps) {
+ const [display, setDisplay] = useState(false);
- const contentRef = createRef();
- const buttonRef = createRef();
+ const contentRef: RefObject = createRef();
+ const buttonRef: RefObject = createRef();
useEffect(() => {
- const handleClickOutside = (event) => {
+ const handleClickOutside = (event: Event) => {
if (
!contentRef.current ||
- contentRef.current.contains(event.target) ||
+ contentRef.current.contains(event.target as Node) ||
!buttonRef.current ||
- buttonRef.current.contains(event.target)
+ buttonRef.current.contains(event.target as Node)
) {
return;
}
@@ -146,13 +183,16 @@ export function Dropdown(props) {
setDisplay(!display);
};
- const getActiveOrDisplay = () => {
- const activeItems = props.options.filter(
- (item) => item.label === props.value || item.value === props.value,
- );
+ const getActiveOrDisplay = (): string => {
+ const activeItems = props.options.filter((item) => {
+ if (Array.isArray(props.value)) {
+ return props.value.some(v => v.label === item.label || v.value === item.value);
+ }
+ return item.label === props.value.label || item.value === props.value.value;
+ });
return activeItems.length === 0 || activeItems.length > 1
? props.display
- : activeItems[0].label;
+ : activeItems[0].label
};
return (
@@ -170,7 +210,6 @@ export function Dropdown(props) {
diff --git a/frontend/src/components/footer/index.js b/frontend/src/components/footer/index.tsx
similarity index 96%
rename from frontend/src/components/footer/index.js
rename to frontend/src/components/footer/index.tsx
index 0aa8118eac..5b7358df77 100644
--- a/frontend/src/components/footer/index.js
+++ b/frontend/src/components/footer/index.tsx
@@ -21,6 +21,7 @@ import {
ORG_PRIVACY_POLICY_URL,
} from '../../config';
import './styles.scss';
+import { RootStore } from '../../store';
const socialNetworks = [
{ link: ORG_TWITTER, icon: },
@@ -32,7 +33,7 @@ const socialNetworks = [
export function Footer() {
const location = useLocation();
- const userDetails = useSelector((state) => state.auth.userDetails);
+ const userDetails = useSelector((state: RootStore) => state.auth.userDetails);
const footerDisabledPaths = [
'projects/:id/tasks',
@@ -69,6 +70,7 @@ export function Footer() {
{getMenuItemsForUser(userDetails).map((item) => (
+ {/* @ts-expect-error TS migrations */}
{!item.serviceDesk ? (
(
+export const RadioField = ({ name, value, className, required = false }: {
+ name: string,
+ value: string,
+ className?: string,
+ required?: boolean,
+}) => (
);
@@ -28,7 +33,13 @@ export const SwitchToggle = ({
onChange,
labelPosition,
small = false,
-}: Object) => (
+}: {
+ label: string,
+ isChecked: boolean,
+ onChange: () => void,
+ labelPosition: string,
+ small?: boolean,
+}) => (
{label && labelPosition !== 'right' &&
{label} }
@@ -46,9 +57,13 @@ export const SwitchToggle = ({
);
-export const OrganisationSelect = ({ className, orgId, onChange }) => {
- const userDetails = useSelector((state) => state.auth.userDetails);
- const token = useSelector((state) => state.auth.token);
+export const OrganisationSelect = ({ className, orgId, onChange }: {
+ className?: string,
+ orgId: number | string,
+ onChange: (value: number | string) => void,
+}) => {
+ const userDetails = useSelector((state: RootStore) => state.auth.userDetails);
+ const token = useSelector((state: RootStore) => state.auth.token);
const [organisations, setOrganisations] = useState([]);
useEffect(() => {
@@ -60,7 +75,7 @@ export const OrganisationSelect = ({ className, orgId, onChange }) => {
}
}, [userDetails, token]);
- const getOrgPlaceholder = (id) => {
+ const getOrgPlaceholder = (id: string | number) => {
const orgs = organisations.filter((org) => org.organisationId === id);
return orgs.length ? orgs[0].name :
;
};
@@ -79,22 +94,26 @@ export const OrganisationSelect = ({ className, orgId, onChange }) => {
);
};
-export function OrganisationSelectInput({ className }) {
+export function OrganisationSelectInput(props: {
+ className?: string;
+ input: { value: string, onChange: (value: string) => void };
+}) {
return (
-
- {(props) => (
- props.input.onChange(value.organisationId || '')}
- className="z-5"
- />
- )}
+
+ props.input.onChange(value.toString())}
+ className="z-5"
+ />
);
}
-export function UserCountrySelect({ className, isDisabled = false }: Object) {
- const locale = useSelector((state) => state.preferences.locale);
+export function UserCountrySelect({ className, isDisabled = false }: {
+ className?: string,
+ isDisabled?: boolean,
+}) {
+ const locale = useSelector((state: RootStore) => state.preferences.locale);
const [options, setOptions] = useState([]);
useEffect(() => {
@@ -103,7 +122,7 @@ export function UserCountrySelect({ className, isDisabled = false }: Object) {
}
}, [locale]);
- const getPlaceholder = (value) => {
+ const getPlaceholder = (value: string) => {
const placeholder = options.filter((option) => option.value === value);
if (placeholder.length) {
return placeholder[0].label;
@@ -130,17 +149,21 @@ export function UserCountrySelect({ className, isDisabled = false }: Object) {
);
}
-export const CheckBoxInput = ({ isActive, changeState, className = '', disabled }) => (
+export const CheckBoxInput = ({ isActive, changeState, className = '', disabled }: {
+ isActive: boolean,
+ changeState: () => void,
+ className?: string,
+ disabled?: boolean,
+}) => (
{} : changeState}
- onKeyPress={disabled ? () => {} : changeState}
- tabIndex="0"
- className={`bg-white w1 h1 ma1 ba bw1 ${
- disabled ? 'b--grey-light' : 'b--red'
- } br1 relative pointer ${className}`}
+ onClick={disabled ? () => { } : changeState}
+ onKeyPress={disabled ? () => { } : changeState}
+ tabIndex={0}
+ className={`bg-white w1 h1 ma1 ba bw1 ${disabled ? 'b--grey-light' : 'b--red'
+ } br1 relative pointer ${className}`}
>
{isActive ? (
);
-export const CheckBox = ({ activeItems, toggleFn, itemId }) => {
+export const CheckBox = ({ activeItems, toggleFn, itemId }: {
+ activeItems: any[];
+ toggleFn: (value: any[]) => void;
+ itemId: any;
+}) => {
const isActive = activeItems.includes(itemId);
const changeState = (e) => {
e.persist();
@@ -185,15 +212,18 @@ export const SelectAll = ({ selected, setSelected, allItems, className }) => {
return
;
};
-export const InterestsList = ({ interests, field, changeSelect }) => (
+export const InterestsList = ({ interests, field, changeSelect }: {
+ interests: any[];
+ field: string;
+ changeSelect: (value: any) => void;
+}) => (
{interests.map((interest) => (
changeSelect(interest.id)}
- className={`${
- interest[field] === true ? 'b--red bw1 blue-dark' : 'b--grey-light blue-grey'
- } bg-white ba br1 tc relative ttc pointer text-break lh-base interest-card `}
+ className={`${interest[field] === true ? 'b--red bw1 blue-dark' : 'b--grey-light blue-grey'
+ } bg-white ba br1 tc relative ttc pointer text-break lh-base interest-card `}
>
{interest.name}
{interest[field] === true && (
@@ -205,7 +235,12 @@ export const InterestsList = ({ interests, field, changeSelect }) => (
);
// Used as a generic search box for input fields in the management section
-export const TextField = ({ value, placeholderMsg, onChange, onCloseIconClick }) => {
+export const TextField = ({ value, placeholderMsg, onChange, onCloseIconClick }: {
+ value: string;
+ placeholderMsg: any;
+ onChange: (e: React.ChangeEvent) => void;
+ onCloseIconClick: () => void;
+}) => {
const inputRef = useRef(null);
return (
@@ -242,9 +277,8 @@ export const TextField = ({ value, placeholderMsg, onChange, onCloseIconClick })
onClick={onCloseIconClick}
role="button"
aria-label="clear"
- className={`absolute w1 h1 top-0 pt2 pointer pr2 right-0 red ${
- !value ? 'pr2 right-0 dn ' : 'pr2 right-0'
- }`}
+ className={`absolute w1 h1 top-0 pt2 pointer pr2 right-0 red ${!value ? 'pr2 right-0 dn ' : 'pr2 right-0'
+ }`}
/>
);
diff --git a/frontend/src/components/header/NavLink.js b/frontend/src/components/header/NavLink.jsx
similarity index 100%
rename from frontend/src/components/header/NavLink.js
rename to frontend/src/components/header/NavLink.jsx
diff --git a/frontend/src/components/header/burgerMenu.js b/frontend/src/components/header/burgerMenu.tsx
similarity index 80%
rename from frontend/src/components/header/burgerMenu.js
rename to frontend/src/components/header/burgerMenu.tsx
index fe326ec014..1e8b5f73fb 100644
--- a/frontend/src/components/header/burgerMenu.js
+++ b/frontend/src/components/header/burgerMenu.tsx
@@ -2,7 +2,9 @@ import { forwardRef } from 'react';
import { MenuIcon, CloseIcon } from '../svgIcons';
-export const BurgerMenu = forwardRef((props, ref) => (
+export const BurgerMenu = forwardRef((props, ref) => (
{
const dispatch = useDispatch();
@@ -31,19 +32,21 @@ export const Header = () => {
const navigate = useNavigate();
const menuItemsContainerRef = useRef(null);
- const userDetails = useSelector((state) => state.auth.userDetails);
- const organisations = useSelector((state) => state.auth.organisations);
- const showOrgBar = useSelector((state) => state.orgBarVisibility.isVisible);
+ const userDetails = useSelector((state: RootStore) => state.auth.userDetails);
+ const organisations = useSelector((state: RootStore) => state.auth.organisations);
+ const showOrgBar = useSelector((state: RootStore) => state.orgBarVisibility.isVisible);
const linkCombo = 'link mh3 barlow-condensed blue-dark f4 ttu lh-solid nowrap pv2';
- const isActive = ({ isPartiallyCurrent }) => {
+ const isActive = ({ isPartiallyCurrent }: {
+ isPartiallyCurrent: boolean;
+ }) => {
return isPartiallyCurrent
? { className: `${linkCombo} bb b--blue-dark bw1` }
: { className: linkCombo };
};
- const getUserLinks = (role) => {
+ const getUserLinks = () => {
return [
{ label: , url: '/settings' },
{ label: , url: '/logout' },
@@ -162,7 +165,7 @@ export const Header = () => {
);
};
-export function getMenuItemsForUser(userDetails, organisations) {
+export function getMenuItemsForUser(userDetails: any, organisations?: any) {
const menuItems = [
{ label: messages.exploreProjects, link: 'explore', showAlways: true },
{
@@ -199,7 +202,9 @@ export function getMenuItemsForUser(userDetails, organisations) {
return filteredMenuItems;
}
-const UserDisplay = ({ username }) => {
+const UserDisplay = ({ username }: {
+ username: string
+}) => {
return (
@@ -208,7 +213,10 @@ const UserDisplay = ({ username }) => {
);
};
-const SignupTrigger = forwardRef((props, ref) => {
+const SignupTrigger = (props: ButtonProps & {
+ signUpStyle: string;
+ alternativeSignUpText?: boolean;
+}) => {
const { signUpStyle, alternativeSignUpText, ...remainingProps } = props;
return (
@@ -219,13 +227,18 @@ const SignupTrigger = forwardRef((props, ref) => {
)}
);
-});
+};
export const AuthButtons = ({
logInStyle,
signUpStyle,
redirectTo,
alternativeSignUpText = false,
+}: {
+ logInStyle: string;
+ signUpStyle: string;
+ redirectTo: string;
+ alternativeSignUpText?: boolean;
}) => {
const [debouncedCreateLoginWindow] = useDebouncedCallback(
(redirectToPass) => createLoginWindow(redirectToPass),
@@ -251,7 +264,12 @@ export const AuthButtons = ({
);
};
-export const ActionItems = ({ userDetails, onUserMenuSelect, location, getUserLinks }) =>
+export const ActionItems = ({ userDetails, onUserMenuSelect, location, getUserLinks }: {
+ userDetails: any;
+ onUserMenuSelect: (e: any) => void;
+ location: any;
+ getUserLinks: (role: string) => any;
+}) =>
userDetails.username ? (
<>
@@ -274,6 +292,7 @@ export const ActionItems = ({ userDetails, onUserMenuSelect, location, getUserLi
>
);
+
export const PopupItems = (props) => {
const dispatch = useDispatch();
diff --git a/frontend/src/components/header/messages.js b/frontend/src/components/header/messages.ts
similarity index 100%
rename from frontend/src/components/header/messages.js
rename to frontend/src/components/header/messages.ts
diff --git a/frontend/src/components/header/notificationBell.js b/frontend/src/components/header/notificationBell.jsx
similarity index 100%
rename from frontend/src/components/header/notificationBell.js
rename to frontend/src/components/header/notificationBell.jsx
diff --git a/frontend/src/components/header/signUp.js b/frontend/src/components/header/signUp.js
deleted file mode 100644
index d56f9b1167..0000000000
--- a/frontend/src/components/header/signUp.js
+++ /dev/null
@@ -1,227 +0,0 @@
-import { useState } from 'react';
-import { FormattedMessage } from 'react-intl';
-
-import messages from './messages';
-import { Button } from '../button';
-import { CloseIcon } from '../svgIcons';
-import { registerUser } from '../../store/actions/user';
-import { store } from '../../store';
-import { createLoginWindow } from '../../utils/login';
-import { ORG_PRIVACY_POLICY_URL, OSM_REGISTER_URL } from '../../config';
-import { setItem } from '../../utils/safe_storage';
-
-export const LoginModal = ({ step, login }) => {
- return (
-
-
- {step.number}.
-
-
-
-
-
login()}>
-
-
-
-
-
-
-
-
-
- );
-};
-
-export const ProceedOSM = ({ data, step, setStep, login }) => {
- const NextStep = (setStep) => {
- window.open(OSM_REGISTER_URL, '_blank');
- setStep((s) => {
- return { ...s, number: 3 };
- });
- };
-
- const handleLogin = () => {
- login();
- setItem('email_address', data.email);
- setItem('name', data.name);
- };
-
- return (
-
-
- {step.number}.
-
-
-
-
-
NextStep(setStep)}>
-
-
-
handleLogin()}>
-
-
-
-
- );
-};
-
-const SignupForm = ({ data, setData, step, setStep }) => {
- const onChange = (e) => {
- setData({ ...data, [e.target.name]: e.target.value });
- if (step.errMessage) setStep({ ...step, errMessage: null });
- };
-
- const checkFields = () => {
- const re =
- // eslint-disable-next-line no-useless-escape
- /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
- if (re.test(data.email) === false) {
- setStep({ ...step, errMessage: });
- return;
- }
-
- const formData = {
- email: data.email,
- };
-
- const registerPromise = store.dispatch(registerUser(formData));
- registerPromise.then((res) => {
- if (res.success === true) {
- setItem('email_address', data.email);
- setItem('name', data.name);
- setStep({ number: 2, errMessage: null });
- } else {
- setStep({ number: 1, errMessage: res.details });
- }
- });
- };
-
- return (
-
-
- {step.number}.
-
-
-
-
-
-
-
-
-
-
-
- {(msg) => {
- return (
-
- );
- }}
-
-
-
-
-
-
-
- {(msg) => {
- return (
-
- );
- }}
-
-
-
- {step.errMessage}
-
-
- {ORG_PRIVACY_POLICY_URL && (
-
-
-
-
-
- )}
-
- checkFields()}
- disabled={step.errMessage !== null || !data.name || !data.email}
- >
-
-
-
-
- );
-};
-
-export const SignUp = ({ closeModal }) => {
- const [data, setData] = useState({ name: '', email: '' });
- const [step, setStep] = useState({ number: 1, errMessage: null });
-
- const login = () => {
- let redirect = '/welcome';
- if (window.location.pathname.startsWith('/projects')) {
- redirect = window.location.pathname;
- }
-
- closeModal();
- createLoginWindow(redirect);
- };
-
- const getStep = (step) => {
- switch (step.number) {
- case 2:
- return ;
- case 3:
- return ;
- default:
- return ;
- }
- };
-
- return (
-
- closeModal()}>
-
-
- {getStep(step)}
-
- );
-};
diff --git a/frontend/src/components/header/signUp.tsx b/frontend/src/components/header/signUp.tsx
new file mode 100644
index 0000000000..ff00117923
--- /dev/null
+++ b/frontend/src/components/header/signUp.tsx
@@ -0,0 +1,266 @@
+import { FormattedMessage } from 'react-intl';
+import messages from './messages';
+import { CloseIcon } from '../svgIcons';
+import { registerUser } from '../../store/actions/user';
+import { store } from '../../store';
+import { ORG_PRIVACY_POLICY_URL, OSM_REGISTER_URL } from '../../config';
+import { setItem } from '../../utils/safe_storage';
+import { Button } from '../button';
+import { useState } from 'react';
+
+export const LoginModal = ({ step, login }: {
+ step: { number: number; errMessage: React.ReactNode };
+ login: () => void;
+}) => {
+ return (
+
+
+ {step.number}.
+
+
+
+
+
+
+
+
+
login()}>
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export const ProceedOSM = ({ data, step, setStep, login }: {
+ data: { email: string; name: string };
+ step: { number: number; errMessage: React.ReactNode };
+ setStep: React.Dispatch>
+ login: () => void;
+}) => {
+ const NextStep = () => {
+ window.open(OSM_REGISTER_URL, '_blank');
+ setStep((s) => {
+ return { ...s, number: 3 };
+ });
+ };
+
+ const handleLogin = () => {
+ login();
+ setItem('email_address', data.email);
+ setItem('name', data.name);
+ };
+
+ return (
+
+
+ {step.number}.
+
+
+
+
+
+
+
+
+
+
+
+
NextStep()}>
+
+
+
handleLogin()}>
+
+
+
+
+
+
+ );
+};
+
+const SignupForm = ({ data, setData, step, setStep }: {
+ data: { email: string; name: string };
+ setData: React.Dispatch>;
+ step: { number: number; errMessage: React.ReactNode };
+ setStep: React.Dispatch>;
+}) => {
+ const onChange = (e: React.ChangeEvent) => {
+ setData({ ...data, [e.target.name]: e.target.value });
+ if (step.errMessage) setStep({ ...step, errMessage: null });
+ };
+
+ const checkFields = () => {
+ const re =
+ // eslint-disable-next-line no-useless-escape
+ /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1, 3}\.[0-9]{1, 3}\.[0-9]{1, 3}\.[0-9]{1, 3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
+ if (re.test(data.email) === false) {
+ setStep({ ...step, errMessage: });
+ return;
+ }
+
+ const formData = {
+ email: data.email,
+ };
+
+ // @ts-expect-error TS Migration
+ const registerPromise = store.dispatch(registerUser(formData));
+ // @ts-expect-error TS Migration
+ registerPromise.then((res) => {
+ if (res.success === true) {
+ setItem('email_address', data.email);
+ setItem('name', data.name);
+ setStep({ number: 2, errMessage: null });
+ } else {
+ setStep({ number: 1, errMessage: res.details });
+ }
+ });
+ };
+
+ return (
+
+
+ {step.number}.
+
+
+
+
+
+
+
+
+
+
+
+ {(msg) => {
+ return (
+
+ );
+ }}
+
+
+
+
+
+
+
+ {(msg) => {
+ return (
+
+ );
+ }}
+
+
+ {step.errMessage}
+
+
+ {
+ ORG_PRIVACY_POLICY_URL && (
+
+
+
+
+
+ )}
+
+ checkFields()}
+ disabled={step.errMessage !== null || !data.name || !data.email}
+ >
+
+
+
+
+
+ );
+};
+
+export const SignUp = ({ closeModal, createLoginWindow }: {
+ closeModal: () => void,
+ createLoginWindow: (redirect: string) => void,
+}) => {
+ const [data, setData] = useState({ name: '', email: '' });
+ const [step, setStep] = useState<{
+ number: number;
+ errMessage: React.ReactNode;
+ }>({ number: 1, errMessage: null });
+
+ const login = () => {
+ let redirect = '/welcome';
+ if (window.location.pathname.startsWith('/projects')) {
+ redirect = window.location.pathname;
+ }
+
+ closeModal();
+ createLoginWindow(redirect);
+ };
+
+ const getStep = (step: {
+ number: number;
+ errMessage: React.ReactNode;
+ }) => {
+ switch (step.number) {
+ case 2:
+ return ;
+ case 3:
+ return ;
+ default:
+ return ;
+ }
+ };
+
+ return (
+
+ closeModal()}>
+
+
+ {getStep(step)}
+
+ );
+};
diff --git a/frontend/src/components/header/updateDialog.js b/frontend/src/components/header/updateDialog.jsx
similarity index 100%
rename from frontend/src/components/header/updateDialog.js
rename to frontend/src/components/header/updateDialog.jsx
diff --git a/frontend/src/components/header/updateEmail.js b/frontend/src/components/header/updateEmail.tsx
similarity index 89%
rename from frontend/src/components/header/updateEmail.js
rename to frontend/src/components/header/updateEmail.tsx
index 6eb44f3eaf..fd7cab4f4c 100644
--- a/frontend/src/components/header/updateEmail.js
+++ b/frontend/src/components/header/updateEmail.tsx
@@ -7,13 +7,16 @@ import messages from './messages';
import { updateUserEmail } from '../../store/actions/auth';
import { PROFILE_RELEVANT_FIELDS } from '../user/forms/personalInformation';
import { ORG_PRIVACY_POLICY_URL } from '../../config';
-import { Button } from '../button';
+import { Button } from '../button.jsx';
+import { RootStore } from '../../store/index.js';
-export const UpdateEmail = ({ closeModal }) => {
+export const UpdateEmail = ({ closeModal }: {
+ closeModal: () => void;
+}) => {
const dispatch = useDispatch();
- const userDetails = useSelector((state) => state.auth.userDetails);
- const token = useSelector((state) => state.auth.token);
+ const userDetails = useSelector((state: RootStore) => state.auth.userDetails);
+ const token = useSelector((state: RootStore) => state.auth.token);
const [userState, setUserState] = useState({ email: '', success: false, details: '' });
const onChange = (e) => {
diff --git a/frontend/src/components/homepage/jumbotron.js b/frontend/src/components/homepage/jumbotron.jsx
similarity index 100%
rename from frontend/src/components/homepage/jumbotron.js
rename to frontend/src/components/homepage/jumbotron.jsx
diff --git a/frontend/src/components/homepage/mappingFlow.js b/frontend/src/components/homepage/mappingFlow.jsx
similarity index 96%
rename from frontend/src/components/homepage/mappingFlow.js
rename to frontend/src/components/homepage/mappingFlow.jsx
index 261b58bb44..1f56217d40 100644
--- a/frontend/src/components/homepage/mappingFlow.js
+++ b/frontend/src/components/homepage/mappingFlow.jsx
@@ -4,7 +4,7 @@ import messages from './messages';
import { MappingIcon, ValidationIcon, DataUseIcon } from '../svgIcons';
import './styles.scss';
-function MappingCard({ image, title, description }: Object) {
+function MappingCard({ image, title, description }) {
return (
diff --git a/frontend/src/components/homepage/stats.js b/frontend/src/components/homepage/stats.jsx
similarity index 97%
rename from frontend/src/components/homepage/stats.js
rename to frontend/src/components/homepage/stats.jsx
index 1901fa35f4..77edfb9ebb 100644
--- a/frontend/src/components/homepage/stats.js
+++ b/frontend/src/components/homepage/stats.jsx
@@ -17,7 +17,7 @@ export const StatsNumber = (props) => {
);
};
-export const StatsColumn = ({ label, value }: Object) => {
+export const StatsColumn = ({ label, value }) => {
return (
diff --git a/frontend/src/components/homepage/testimonials.js b/frontend/src/components/homepage/testimonials.jsx
similarity index 100%
rename from frontend/src/components/homepage/testimonials.js
rename to frontend/src/components/homepage/testimonials.jsx
diff --git a/frontend/src/components/homepage/whoIsMapping.js b/frontend/src/components/homepage/whoIsMapping.jsx
similarity index 100%
rename from frontend/src/components/homepage/whoIsMapping.js
rename to frontend/src/components/homepage/whoIsMapping.jsx
diff --git a/frontend/src/components/horizontalScroll/index.js b/frontend/src/components/horizontalScroll/index.tsx
similarity index 71%
rename from frontend/src/components/horizontalScroll/index.js
rename to frontend/src/components/horizontalScroll/index.tsx
index 04216b2035..7350c59c47 100644
--- a/frontend/src/components/horizontalScroll/index.js
+++ b/frontend/src/components/horizontalScroll/index.tsx
@@ -1,4 +1,4 @@
-import { useState, useEffect } from 'react';
+import { useState, useEffect, RefObject } from 'react';
import { ChevronRightIcon } from '../svgIcons';
import { useWindowSize } from '../../hooks/UseWindowSize';
@@ -10,6 +10,12 @@ export function HorizontalScroll({
containerClass,
style = {},
children,
+}: {
+ className?: string;
+ menuItemsContainerRef: RefObject
;
+ containerClass: string;
+ style?: React.CSSProperties;
+ children: React.ReactNode;
}) {
const [scrollLeft, setScrollLeft] = useState(0);
// This triggers rerender when the screen size changes, so had to keep it
@@ -26,11 +32,12 @@ export function HorizontalScroll({
};
}, [containerClass]);
- const updateScrollLeft = (e) => {
+ const updateScrollLeft = (e: Event) => {
+ // @ts-expect-error TS Migrations
setScrollLeft(e.target.scrollLeft);
};
- const handleScroll = (direction) => {
+ const handleScroll = (direction: "left" | "right") => {
let currentScroll = scrollLeft;
if (direction === 'right') {
currentScroll += 200;
@@ -46,9 +53,8 @@ export function HorizontalScroll({
return (
0 ? 'flex items-center' : 'dn'
- }`}
+ className={`bg-white left-icon absolute h-100 left-0 top-0 rotate-180 z-1 h-100 pointer pa2 translate-icon-btm ${scrollLeft > 0 ? 'flex items-center' : 'dn'
+ }`}
role="button"
onClick={() => handleScroll('left')}
>
@@ -56,14 +62,13 @@ export function HorizontalScroll({
handleScroll('right')}
>
diff --git a/frontend/src/components/interests/index.js b/frontend/src/components/interests/index.js
index b0227f8915..d782090be1 100644
--- a/frontend/src/components/interests/index.js
+++ b/frontend/src/components/interests/index.js
@@ -7,7 +7,7 @@ import ReactPlaceholder from 'react-placeholder';
import messages from '../teamsAndOrgs/messages';
import { Management } from '../teamsAndOrgs/management';
import { HashtagIcon } from '../svgIcons';
-import { Button } from '../button';
+import { Button } from '../button.jsx';
import { nCardPlaceholders } from '../teamsAndOrgs/campaignsPlaceholder';
import { TextField } from '../formInputs';
diff --git a/frontend/src/components/licenses/index.js b/frontend/src/components/licenses/index.js
index 7ec1db9981..f5676c037f 100644
--- a/frontend/src/components/licenses/index.js
+++ b/frontend/src/components/licenses/index.js
@@ -7,7 +7,7 @@ import ReactPlaceholder from 'react-placeholder';
import messages from '../teamsAndOrgs/messages';
import { Management } from '../teamsAndOrgs/management';
import { CopyrightIcon } from '../svgIcons';
-import { Button } from '../button';
+import { Button } from '../button.jsx';
import { nCardPlaceholders } from './licensesPlaceholder';
import { TextField } from '../formInputs';
diff --git a/frontend/src/components/localeSelect.js b/frontend/src/components/localeSelect.tsx
similarity index 81%
rename from frontend/src/components/localeSelect.js
rename to frontend/src/components/localeSelect.tsx
index 5c716723a9..7ebc43fa7d 100644
--- a/frontend/src/components/localeSelect.js
+++ b/frontend/src/components/localeSelect.tsx
@@ -5,6 +5,7 @@ import Select from 'react-select';
import messages from './messages';
import { supportedLocales } from '../utils/internationalization';
import { setLocale } from '../store/actions/userPreferences';
+import { RootStore } from '../store';
function LocaleSelect({
className,
@@ -12,14 +13,26 @@ function LocaleSelect({
setLocale,
removeBorder = true,
fullWidth = false,
+}: {
+ className?: string;
+ userPreferences: { locale: string };
+ setLocale: (locale: string) => void;
+ removeBorder?: boolean;
+ fullWidth?: boolean;
}) {
- const onLocaleSelect = (arr) => {
+ const onLocaleSelect = (arr: {
+ value: string;
+ label: string;
+ }[]) => {
setLocale(arr[0].value);
};
const getActiveLanguageNames = () => {
const locales = [userPreferences.locale, navigator.language, navigator.language.substr(0, 2)];
- let supportedLocaleNames = [];
+ const supportedLocaleNames: {
+ value: string;
+ label: string
+ }[] = [];
locales.forEach((locale) =>
supportedLocales
.filter((i) => i.value === locale)
@@ -55,7 +68,7 @@ function LocaleSelect({
);
}
-const mapStateToProps = (state) => ({
+const mapStateToProps = (state: RootStore) => ({
userPreferences: state.preferences,
});
diff --git a/frontend/src/components/messages.js b/frontend/src/components/messages.ts
similarity index 100%
rename from frontend/src/components/messages.js
rename to frontend/src/components/messages.ts
diff --git a/frontend/src/components/notifications/actionButtons.js b/frontend/src/components/notifications/actionButtons.jsx
similarity index 96%
rename from frontend/src/components/notifications/actionButtons.js
rename to frontend/src/components/notifications/actionButtons.jsx
index d2f2c96f33..49e9ea708e 100644
--- a/frontend/src/components/notifications/actionButtons.js
+++ b/frontend/src/components/notifications/actionButtons.jsx
@@ -84,9 +84,9 @@ export const ActionButtons = ({
isAllSelected
? updateUnreadCount()
: // The decrement count is readily available; deducting count from selected
- Array.from({ length: unreadCountInSelected }, () =>
- dispatch({ type: 'DECREMENT_UNREAD_COUNT' }),
- );
+ Array.from({ length: unreadCountInSelected }, () =>
+ dispatch({ type: 'DECREMENT_UNREAD_COUNT' }),
+ );
}
if (selected.length) {
diff --git a/frontend/src/components/notifications/inboxNav.js b/frontend/src/components/notifications/inboxNav.jsx
similarity index 100%
rename from frontend/src/components/notifications/inboxNav.js
rename to frontend/src/components/notifications/inboxNav.jsx
diff --git a/frontend/src/components/notifications/messages.js b/frontend/src/components/notifications/messages.ts
similarity index 100%
rename from frontend/src/components/notifications/messages.js
rename to frontend/src/components/notifications/messages.ts
diff --git a/frontend/src/components/notifications/notificationBodyCard.js b/frontend/src/components/notifications/notificationBodyCard.jsx
similarity index 99%
rename from frontend/src/components/notifications/notificationBodyCard.js
rename to frontend/src/components/notifications/notificationBodyCard.jsx
index 27bc692e06..00e2c17dd5 100644
--- a/frontend/src/components/notifications/notificationBodyCard.js
+++ b/frontend/src/components/notifications/notificationBodyCard.jsx
@@ -87,7 +87,7 @@ export function NotificationBodyCard({
message,
sentDate,
},
-}: Object) {
+}) {
const token = useSelector((state) => state.auth.token);
const { value, unit } = selectUnit(new Date((sentDate && new Date(sentDate)) || new Date()));
const showASendingUser =
diff --git a/frontend/src/components/notifications/notificationCard.js b/frontend/src/components/notifications/notificationCard.jsx
similarity index 99%
rename from frontend/src/components/notifications/notificationCard.js
rename to frontend/src/components/notifications/notificationCard.jsx
index b5e1486e7b..1f830ba50d 100644
--- a/frontend/src/components/notifications/notificationCard.js
+++ b/frontend/src/components/notifications/notificationCard.jsx
@@ -25,7 +25,7 @@ export const stripHtmlToText = (notificationHtml) =>
export const typesThatUseSystemAvatar = ['SYSTEM', 'REQUEST_TEAM_NOTIFICATION'];
-export const MessageAvatar = ({ messageType, fromUsername, displayPictureUrl, size }: Object) => {
+export const MessageAvatar = ({ messageType, fromUsername, displayPictureUrl, size }) => {
const checkIsSystem = typesThatUseSystemAvatar.indexOf(messageType) !== -1;
if (!fromUsername && !checkIsSystem) {
@@ -68,7 +68,7 @@ export function NotificationCard({
retryFn,
selected,
setSelected,
-}: Object) {
+}) {
const dispatch = useDispatch();
const token = useSelector((state) => state.auth.token);
const ref = useRef();
@@ -201,7 +201,7 @@ export function NotificationCardMini({
setPopoutFocus,
retryFn,
read,
-}: Object) {
+}) {
const dispatch = useDispatch();
const setMessageAsRead = () => {
diff --git a/frontend/src/components/notifications/notificationOrderBy.js b/frontend/src/components/notifications/notificationOrderBy.jsx
similarity index 100%
rename from frontend/src/components/notifications/notificationOrderBy.js
rename to frontend/src/components/notifications/notificationOrderBy.jsx
diff --git a/frontend/src/components/notifications/notificationPlaceholder.js b/frontend/src/components/notifications/notificationPlaceholder.jsx
similarity index 100%
rename from frontend/src/components/notifications/notificationPlaceholder.js
rename to frontend/src/components/notifications/notificationPlaceholder.jsx
diff --git a/frontend/src/components/notifications/notificationResults.js b/frontend/src/components/notifications/notificationResults.jsx
similarity index 100%
rename from frontend/src/components/notifications/notificationResults.js
rename to frontend/src/components/notifications/notificationResults.jsx
diff --git a/frontend/src/components/notifications/paginator.js b/frontend/src/components/notifications/paginator.jsx
similarity index 100%
rename from frontend/src/components/notifications/paginator.js
rename to frontend/src/components/notifications/paginator.jsx
diff --git a/frontend/src/components/notifications/selectAllNotifications.js b/frontend/src/components/notifications/selectAllNotifications.jsx
similarity index 100%
rename from frontend/src/components/notifications/selectAllNotifications.js
rename to frontend/src/components/notifications/selectAllNotifications.jsx
diff --git a/frontend/src/components/paginator/index.js b/frontend/src/components/paginator/index.jsx
similarity index 98%
rename from frontend/src/components/paginator/index.js
rename to frontend/src/components/paginator/index.jsx
index 7dd5742cce..80d1e90be4 100644
--- a/frontend/src/components/paginator/index.js
+++ b/frontend/src/components/paginator/index.jsx
@@ -66,7 +66,7 @@ export const PageButton = (props) => {
}
};
-export function PaginatorLine({ activePage, lastPage, setPageFn, className }: Object) {
+export function PaginatorLine({ activePage, lastPage, setPageFn, className }) {
const pageOptions = listPageOptions(activePage, lastPage);
return (
diff --git a/frontend/src/components/partners/currentProjects.js b/frontend/src/components/partners/currentProjects.js
index ee88fe296e..3236bc8c01 100644
--- a/frontend/src/components/partners/currentProjects.js
+++ b/frontend/src/components/partners/currentProjects.js
@@ -6,7 +6,7 @@ import { Pagination } from 'swiper/modules';
import { Swiper, SwiperSlide } from 'swiper/react';
import { TasksMap } from '../taskSelection/map';
-import { Button } from '../button';
+import { Button } from '../button.jsx';
import { API_URL } from '../../config';
import messages from './messages';
import ProjectProgressBar from '../projectCard/projectProgressBar';
@@ -113,7 +113,7 @@ export function CurrentProjects({ currentProjects }) {
{project.info && project.info.name}
diff --git a/frontend/src/components/preloader.js b/frontend/src/components/preloader.tsx
similarity index 100%
rename from frontend/src/components/preloader.js
rename to frontend/src/components/preloader.tsx
diff --git a/frontend/src/components/projectCreate/messages.js b/frontend/src/components/projectCreate/messages.ts
similarity index 100%
rename from frontend/src/components/projectCreate/messages.js
rename to frontend/src/components/projectCreate/messages.ts
diff --git a/frontend/src/components/projectCreate/navButtons.js b/frontend/src/components/projectCreate/navButtons.tsx
similarity index 82%
rename from frontend/src/components/projectCreate/navButtons.js
rename to frontend/src/components/projectCreate/navButtons.tsx
index eb80376edf..5456cc675e 100644
--- a/frontend/src/components/projectCreate/navButtons.js
+++ b/frontend/src/components/projectCreate/navButtons.tsx
@@ -2,10 +2,25 @@ import { featureCollection } from '@turf/helpers';
import { FormattedMessage, useIntl } from 'react-intl';
import messages from './messages';
-import { Button } from '../button';
+import { Button } from '../button.jsx';
import { useAsync } from '../../hooks/UseAsync';
-const clearParamsStep = (props) => {
+const clearParamsStep = (props: {
+ index: number;
+ metadata: {
+ area: number;
+ arbitraryTasks: boolean;
+ geom: any;
+ taskGrid: any;
+ tempTaskGrid: any;
+ tasksNumber: number;
+ };
+ maxArea: number;
+ mapObj: any;
+ setStep: (step: number) => void;
+ updateMetadata: (metadata: any) => void;
+ setErr: (err: { error: boolean; message: string }) => void;
+}) => {
switch (props.index) {
case 2: //clear Tasks
props.mapObj.map.getSource('grid').setData(featureCollection([]));
@@ -39,7 +54,7 @@ const clearParamsStep = (props) => {
props.setStep(prevStep);
};
-const NavButtons = (props) => {
+const NavButtons = (props: any) => {
const intl = useIntl();
const createProjectFn = () => {
@@ -47,7 +62,21 @@ const NavButtons = (props) => {
};
const createProjectAsync = useAsync(createProjectFn);
- const validateStep = (props) => {
+ const validateStep = (props: {
+ index: number;
+ metadata: {
+ area: number;
+ arbitraryTasks: boolean;
+ geom: any;
+ taskGrid: any;
+ tempTaskGrid: any;
+ tasksNumber: number;
+ };
+ maxArea: number;
+ mapObj: any;
+ setStep: (step: number) => void;
+ updateMetadata: (metadata: any) => void;
+ }) => {
switch (props.index) {
case 1: // Set Project AOI.
if (props.metadata.area >= props.maxArea) {
@@ -90,7 +119,7 @@ const NavButtons = (props) => {
props.setStep(nextStep);
return { error: false, message: '' };
};
- const stepHandler = (event) => {
+ const stepHandler = () => {
const resp = validateStep(props);
props.setErr(resp);
};
diff --git a/frontend/src/components/projectDetail/footer.js b/frontend/src/components/projectDetail/footer.js
index 05f8fe1910..aeab6719ef 100644
--- a/frontend/src/components/projectDetail/footer.js
+++ b/frontend/src/components/projectDetail/footer.js
@@ -3,7 +3,7 @@ import { Link } from 'react-router-dom';
import { useSelector } from 'react-redux';
import { FormattedMessage } from 'react-intl';
-import { Button } from '../button';
+import { Button } from '../button.jsx';
import messages from './messages';
import { ShareButton } from './shareButton';
import { AddToFavorites } from './favorites';
@@ -61,9 +61,8 @@ export const ProjectDetailFooter = ({ className, projectId }) => {
return (
{/* TODO ADD ANCHORS */}
diff --git a/frontend/src/components/projectDetail/messages.js b/frontend/src/components/projectDetail/messages.ts
similarity index 100%
rename from frontend/src/components/projectDetail/messages.js
rename to frontend/src/components/projectDetail/messages.ts
diff --git a/frontend/src/components/projectDetail/privateProjectError.js b/frontend/src/components/projectDetail/privateProjectError.js
index c357b8f53d..fd01c84d7d 100644
--- a/frontend/src/components/projectDetail/privateProjectError.js
+++ b/frontend/src/components/projectDetail/privateProjectError.js
@@ -3,7 +3,7 @@ import { FormattedMessage } from 'react-intl';
import messages from './messages';
import { LockIcon } from '../svgIcons';
-import { Button } from '../button';
+import { Button } from '../button.jsx';
const PrivateProjectError = () => {
const navigate = useNavigate();
diff --git a/frontend/src/components/projectDetail/questionsAndComments.js b/frontend/src/components/projectDetail/questionsAndComments.js
index 4fb083ef64..d69ac509d3 100644
--- a/frontend/src/components/projectDetail/questionsAndComments.js
+++ b/frontend/src/components/projectDetail/questionsAndComments.js
@@ -7,7 +7,7 @@ import ReactPlaceholder from 'react-placeholder';
import messages from './messages';
import { RelativeTimeWithUnit } from '../../utils/formattedRelativeTime';
import { PaginatorLine } from '../paginator';
-import { Button } from '../button';
+import { Button } from '../button.jsx';
import { Alert } from '../alert';
import { MessageStatus } from '../comments/status';
import { UserAvatar } from '../user/avatar';
diff --git a/frontend/src/components/projectDetail/statusBox.js b/frontend/src/components/projectDetail/statusBox.tsx
similarity index 70%
rename from frontend/src/components/projectDetail/statusBox.js
rename to frontend/src/components/projectDetail/statusBox.tsx
index 35d1a27343..a073d078ce 100644
--- a/frontend/src/components/projectDetail/statusBox.js
+++ b/frontend/src/components/projectDetail/statusBox.tsx
@@ -2,7 +2,10 @@ import { FormattedMessage } from 'react-intl';
import messages from './messages';
-export const ProjectStatusBox = ({ status, className }: Object) => {
+export const ProjectStatusBox = ({ status, className }: {
+ status: "DRAFT" | "PUBLISHED" | "ARCHIVED",
+ className: string,
+}) => {
const colour = status === 'DRAFT' ? 'orange' : 'blue-grey';
return (
diff --git a/frontend/src/components/projectDetail/timeline.js b/frontend/src/components/projectDetail/timeline.tsx
similarity index 93%
rename from frontend/src/components/projectDetail/timeline.js
rename to frontend/src/components/projectDetail/timeline.tsx
index c62efbb854..c32b410247 100644
--- a/frontend/src/components/projectDetail/timeline.js
+++ b/frontend/src/components/projectDetail/timeline.tsx
@@ -28,7 +28,11 @@ ChartJS.register(
Tooltip,
);
-export default function ProjectTimeline({ tasksByDay }: Object) {
+export default function ProjectTimeline({ tasksByDay }: {
+ tasksByDay: {
+ date: string;
+ }[]
+}) {
const intl = useIntl();
const unit = useTimeDiff(tasksByDay);
const mappedTasksConfig = {
diff --git a/frontend/src/components/projectEdit/actionsForm.js b/frontend/src/components/projectEdit/actionsForm.js
index d9f1561027..f1a25b2393 100644
--- a/frontend/src/components/projectEdit/actionsForm.js
+++ b/frontend/src/components/projectEdit/actionsForm.js
@@ -6,7 +6,7 @@ import { useNavigate } from 'react-router-dom';
import { FormattedMessage } from 'react-intl';
import messages from './messages';
-import { Button } from '../button';
+import { Button } from '../button.jsx';
import { Alert } from '../alert';
import { DeleteModal } from '../deleteModal';
import { styleClasses, StateContext } from '../../views/projectEdit';
diff --git a/frontend/src/components/projectEdit/partnersForm.js b/frontend/src/components/projectEdit/partnersForm.js
index 4cc2ab1b79..428a33c359 100644
--- a/frontend/src/components/projectEdit/partnersForm.js
+++ b/frontend/src/components/projectEdit/partnersForm.js
@@ -12,7 +12,7 @@ import PropTypes from 'prop-types';
import messages from './messages';
import { Alert } from '../alert';
import { ChevronDownIcon, CloseIcon } from '../svgIcons';
-import { Button } from '../button';
+import { Button } from '../button.jsx';
import { styleClasses } from '../../views/projectEdit';
import { pushToLocalJSONAPI } from '../../network/genericJSONRequest';
import { useAllPartnersQuery } from '../../api/projects';
diff --git a/frontend/src/components/projectEdit/partnersListing.js b/frontend/src/components/projectEdit/partnersListing.js
index 80558f9835..1b7f68dad0 100644
--- a/frontend/src/components/projectEdit/partnersListing.js
+++ b/frontend/src/components/projectEdit/partnersListing.js
@@ -14,7 +14,7 @@ import PropTypes from 'prop-types';
import messages from './messages';
import { Alert } from '../alert';
import { BanIcon, CircleMinusIcon, CircleExclamationIcon } from '../svgIcons';
-import { Button } from '../button';
+import { Button } from '../button.jsx';
import { styleClasses } from '../../views/projectEdit';
import { fetchLocalJSONAPI, pushToLocalJSONAPI } from '../../network/genericJSONRequest';
import { DateCustomInput } from './partnersForm';
diff --git a/frontend/src/components/projectEdit/teamSelect.js b/frontend/src/components/projectEdit/teamSelect.js
index 3a97b3fafc..14b9e5ec09 100644
--- a/frontend/src/components/projectEdit/teamSelect.js
+++ b/frontend/src/components/projectEdit/teamSelect.js
@@ -4,7 +4,7 @@ import { FormattedMessage, useIntl } from 'react-intl';
import messages from './messages';
import commonMessages from '../messages';
-import { Button } from '../button';
+import { Button } from '../button.jsx';
import { StateContext } from '../../views/projectEdit';
import { PencilIcon, WasteIcon, ExternalLinkIcon } from '../svgIcons';
import { useFetchWithAbort } from '../../hooks/UseFetch';
diff --git a/frontend/src/components/projects/clearFilters.js b/frontend/src/components/projects/clearFilters.tsx
similarity index 72%
rename from frontend/src/components/projects/clearFilters.js
rename to frontend/src/components/projects/clearFilters.tsx
index 2104594b24..85af8d55d7 100644
--- a/frontend/src/components/projects/clearFilters.js
+++ b/frontend/src/components/projects/clearFilters.tsx
@@ -3,7 +3,10 @@ import { FormattedMessage } from 'react-intl';
import messages from './messages';
-export default function ClearFilters({ url, className = '' }: Object) {
+export default function ClearFilters({ url, className = '' }: {
+ url: string;
+ className?: string;
+}) {
return (
diff --git a/frontend/src/components/projects/filterSelectFields.js b/frontend/src/components/projects/filterSelectFields.jsx
similarity index 100%
rename from frontend/src/components/projects/filterSelectFields.js
rename to frontend/src/components/projects/filterSelectFields.jsx
diff --git a/frontend/src/components/projects/list.js b/frontend/src/components/projects/list.jsx
similarity index 100%
rename from frontend/src/components/projects/list.js
rename to frontend/src/components/projects/list.jsx
diff --git a/frontend/src/components/projects/mappingTypeFilterPicker.js b/frontend/src/components/projects/mappingTypeFilterPicker.jsx
similarity index 100%
rename from frontend/src/components/projects/mappingTypeFilterPicker.js
rename to frontend/src/components/projects/mappingTypeFilterPicker.jsx
diff --git a/frontend/src/components/projects/messages.js b/frontend/src/components/projects/messages.ts
similarity index 100%
rename from frontend/src/components/projects/messages.js
rename to frontend/src/components/projects/messages.ts
diff --git a/frontend/src/components/projects/moreFiltersForm.js b/frontend/src/components/projects/moreFiltersForm.jsx
similarity index 99%
rename from frontend/src/components/projects/moreFiltersForm.js
rename to frontend/src/components/projects/moreFiltersForm.jsx
index 3545a214b0..406d05cdc9 100644
--- a/frontend/src/components/projects/moreFiltersForm.js
+++ b/frontend/src/components/projects/moreFiltersForm.jsx
@@ -4,7 +4,7 @@ import { useQueryParam, BooleanParam } from 'use-query-params';
import { FormattedMessage } from 'react-intl';
import messages from './messages';
-import { Button } from '../button';
+import { Button } from '../button.jsx';
import { SwitchToggle } from '../formInputs';
import { useTagAPI } from '../../hooks/UseTagAPI';
import { useExploreProjectsQueryParams } from '../../hooks/UseProjectsQueryAPI';
diff --git a/frontend/src/components/projects/myProjectNav.js b/frontend/src/components/projects/myProjectNav.jsx
similarity index 100%
rename from frontend/src/components/projects/myProjectNav.js
rename to frontend/src/components/projects/myProjectNav.jsx
diff --git a/frontend/src/components/projects/orderBy.js b/frontend/src/components/projects/orderBy.jsx
similarity index 100%
rename from frontend/src/components/projects/orderBy.js
rename to frontend/src/components/projects/orderBy.jsx
diff --git a/frontend/src/components/projects/projectCardPaginator.js b/frontend/src/components/projects/projectCardPaginator.jsx
similarity index 100%
rename from frontend/src/components/projects/projectCardPaginator.js
rename to frontend/src/components/projects/projectCardPaginator.jsx
diff --git a/frontend/src/components/projects/projectNav.js b/frontend/src/components/projects/projectNav.jsx
similarity index 100%
rename from frontend/src/components/projects/projectNav.js
rename to frontend/src/components/projects/projectNav.jsx
diff --git a/frontend/src/components/projects/projectSearchBox.js b/frontend/src/components/projects/projectSearchBox.jsx
similarity index 100%
rename from frontend/src/components/projects/projectSearchBox.js
rename to frontend/src/components/projects/projectSearchBox.jsx
diff --git a/frontend/src/components/projects/projectSearchResults.js b/frontend/src/components/projects/projectSearchResults.jsx
similarity index 100%
rename from frontend/src/components/projects/projectSearchResults.js
rename to frontend/src/components/projects/projectSearchResults.jsx
diff --git a/frontend/src/components/projects/projectsActionFilter.js b/frontend/src/components/projects/projectsActionFilter.jsx
similarity index 100%
rename from frontend/src/components/projects/projectsActionFilter.js
rename to frontend/src/components/projects/projectsActionFilter.jsx
diff --git a/frontend/src/components/statsTimestamp/index.js b/frontend/src/components/statsTimestamp/index.jsx
similarity index 100%
rename from frontend/src/components/statsTimestamp/index.js
rename to frontend/src/components/statsTimestamp/index.jsx
diff --git a/frontend/src/components/statsTimestamp/messages.js b/frontend/src/components/statsTimestamp/messages.ts
similarity index 100%
rename from frontend/src/components/statsTimestamp/messages.js
rename to frontend/src/components/statsTimestamp/messages.ts
diff --git a/frontend/src/components/svgIcons/alert.js b/frontend/src/components/svgIcons/alert.js
deleted file mode 100644
index 4085c6e251..0000000000
--- a/frontend/src/components/svgIcons/alert.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import { PureComponent } from 'react';
-
-export class AlertIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/alert.tsx b/frontend/src/components/svgIcons/alert.tsx
new file mode 100644
index 0000000000..b2523853d4
--- /dev/null
+++ b/frontend/src/components/svgIcons/alert.tsx
@@ -0,0 +1,12 @@
+import { HTMLProps } from 'react';
+
+export const AlertIcon = (props: HTMLProps
) => {
+ return (
+
+
+
+ );
+}
diff --git a/frontend/src/components/svgIcons/area.js b/frontend/src/components/svgIcons/area.js
deleted file mode 100644
index df16345bd9..0000000000
--- a/frontend/src/components/svgIcons/area.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import { PureComponent } from 'react';
-
-export class AreaIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/area.tsx b/frontend/src/components/svgIcons/area.tsx
new file mode 100644
index 0000000000..f74378e390
--- /dev/null
+++ b/frontend/src/components/svgIcons/area.tsx
@@ -0,0 +1,28 @@
+import { HTMLProps } from 'react';
+
+export const AreaIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/asterisk.js b/frontend/src/components/svgIcons/asterisk.js
deleted file mode 100644
index 5f8de49bc1..0000000000
--- a/frontend/src/components/svgIcons/asterisk.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { PureComponent } from 'react';
-
-// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
-// License: CC-By 4.0
-export class AsteriskIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/asterisk.tsx b/frontend/src/components/svgIcons/asterisk.tsx
new file mode 100644
index 0000000000..3934ff1f95
--- /dev/null
+++ b/frontend/src/components/svgIcons/asterisk.tsx
@@ -0,0 +1,12 @@
+import { HTMLProps } from 'react';
+
+// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
+// License: CC-By 4.0
+export const AsteriskIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/ban.js b/frontend/src/components/svgIcons/ban.js
deleted file mode 100644
index 0daa94515a..0000000000
--- a/frontend/src/components/svgIcons/ban.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { PureComponent } from 'react';
-
-// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
-// License: CC-By 4.0
-export class BanIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/ban.tsx b/frontend/src/components/svgIcons/ban.tsx
new file mode 100644
index 0000000000..8d5a7d282b
--- /dev/null
+++ b/frontend/src/components/svgIcons/ban.tsx
@@ -0,0 +1,12 @@
+import { HTMLProps, PureComponent } from 'react';
+
+// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
+// License: CC-By 4.0
+export const BanIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/bell.js b/frontend/src/components/svgIcons/bell.js
deleted file mode 100644
index c09df19c18..0000000000
--- a/frontend/src/components/svgIcons/bell.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { PureComponent } from 'react';
-
-export class BellIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/bell.tsx b/frontend/src/components/svgIcons/bell.tsx
new file mode 100644
index 0000000000..72708b41cb
--- /dev/null
+++ b/frontend/src/components/svgIcons/bell.tsx
@@ -0,0 +1,12 @@
+import { HTMLProps } from "react";
+
+export const BellIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/calendar.js b/frontend/src/components/svgIcons/calendar.js
deleted file mode 100644
index a66725b5a7..0000000000
--- a/frontend/src/components/svgIcons/calendar.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { PureComponent } from 'react';
-
-// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
-// License: CC-By 4.0
-export class CalendarIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/calendar.tsx b/frontend/src/components/svgIcons/calendar.tsx
new file mode 100644
index 0000000000..1728509c31
--- /dev/null
+++ b/frontend/src/components/svgIcons/calendar.tsx
@@ -0,0 +1,12 @@
+import { HTMLProps } from 'react';
+
+// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
+// License: CC-By 4.0
+export const CalendarIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/chart.js b/frontend/src/components/svgIcons/chart.js
deleted file mode 100644
index 05fae045e3..0000000000
--- a/frontend/src/components/svgIcons/chart.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { PureComponent } from 'react';
-
-// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
-// License: CC-By 4.0
-export class ChartLineIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/chart.tsx b/frontend/src/components/svgIcons/chart.tsx
new file mode 100644
index 0000000000..7a9b227e18
--- /dev/null
+++ b/frontend/src/components/svgIcons/chart.tsx
@@ -0,0 +1,12 @@
+import { HTMLProps } from 'react';
+
+// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
+// License: CC-By 4.0
+export const ChartLineIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/check.js b/frontend/src/components/svgIcons/check.js
deleted file mode 100644
index e9fcdd1a43..0000000000
--- a/frontend/src/components/svgIcons/check.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import { PureComponent } from 'react';
-
-export class CheckIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/check.tsx b/frontend/src/components/svgIcons/check.tsx
new file mode 100644
index 0000000000..6e18674c08
--- /dev/null
+++ b/frontend/src/components/svgIcons/check.tsx
@@ -0,0 +1,11 @@
+import { HTMLProps } from 'react';
+
+export const CheckIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/chevron-down.js b/frontend/src/components/svgIcons/chevron-down.js
deleted file mode 100644
index 21bd74b378..0000000000
--- a/frontend/src/components/svgIcons/chevron-down.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import { PureComponent } from 'react';
-
-export class ChevronDownIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/chevron-down.tsx b/frontend/src/components/svgIcons/chevron-down.tsx
new file mode 100644
index 0000000000..00e8c5cb54
--- /dev/null
+++ b/frontend/src/components/svgIcons/chevron-down.tsx
@@ -0,0 +1,10 @@
+import { HTMLProps } from "react";
+
+export const ChevronDownIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/chevron-right.js b/frontend/src/components/svgIcons/chevron-right.js
deleted file mode 100644
index c75175cb04..0000000000
--- a/frontend/src/components/svgIcons/chevron-right.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import { PureComponent } from 'react';
-
-export class ChevronRightIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/chevron-right.tsx b/frontend/src/components/svgIcons/chevron-right.tsx
new file mode 100644
index 0000000000..f9de898066
--- /dev/null
+++ b/frontend/src/components/svgIcons/chevron-right.tsx
@@ -0,0 +1,10 @@
+import { HTMLProps } from "react";
+
+export const ChevronRightIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/chevron-up.js b/frontend/src/components/svgIcons/chevron-up.js
deleted file mode 100644
index 850120d1c1..0000000000
--- a/frontend/src/components/svgIcons/chevron-up.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import { PureComponent } from 'react';
-
-export class ChevronUpIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/chevron-up.tsx b/frontend/src/components/svgIcons/chevron-up.tsx
new file mode 100644
index 0000000000..eadf5aeecf
--- /dev/null
+++ b/frontend/src/components/svgIcons/chevron-up.tsx
@@ -0,0 +1,10 @@
+import { HTMLProps } from "react";
+
+export const ChevronUpIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/circle.js b/frontend/src/components/svgIcons/circle.js
deleted file mode 100644
index c778c06b3c..0000000000
--- a/frontend/src/components/svgIcons/circle.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { PureComponent } from 'react';
-
-// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
-// License: CC-By 4.0
-export class CircleIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/circle.tsx b/frontend/src/components/svgIcons/circle.tsx
new file mode 100644
index 0000000000..09132044d9
--- /dev/null
+++ b/frontend/src/components/svgIcons/circle.tsx
@@ -0,0 +1,12 @@
+import { HTMLProps } from "react";
+
+// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
+// License: CC-By 4.0
+export const CircleIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/circleExclamation.js b/frontend/src/components/svgIcons/circleExclamation.js
deleted file mode 100644
index 4163d4fa03..0000000000
--- a/frontend/src/components/svgIcons/circleExclamation.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import { PureComponent } from 'react';
-
-export class CircleExclamationIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/circleExclamation.tsx b/frontend/src/components/svgIcons/circleExclamation.tsx
new file mode 100644
index 0000000000..9e11301a55
--- /dev/null
+++ b/frontend/src/components/svgIcons/circleExclamation.tsx
@@ -0,0 +1,17 @@
+import { HTMLProps } from 'react';
+
+export const CircleExclamationIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/circleMinus.js b/frontend/src/components/svgIcons/circleMinus.js
deleted file mode 100644
index 51c67bb61a..0000000000
--- a/frontend/src/components/svgIcons/circleMinus.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import { PureComponent } from 'react';
-
-export class CircleMinusIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/circleMinus.tsx b/frontend/src/components/svgIcons/circleMinus.tsx
new file mode 100644
index 0000000000..e752f28816
--- /dev/null
+++ b/frontend/src/components/svgIcons/circleMinus.tsx
@@ -0,0 +1,17 @@
+import { HTMLProps } from 'react';
+
+export const CircleMinusIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/clipboard.js b/frontend/src/components/svgIcons/clipboard.js
deleted file mode 100644
index 8c1e94d192..0000000000
--- a/frontend/src/components/svgIcons/clipboard.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { PureComponent } from 'react';
-
-// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
-// License: CC-By 4.0
-export class ClipboardIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/clipboard.tsx b/frontend/src/components/svgIcons/clipboard.tsx
new file mode 100644
index 0000000000..b338f48aa3
--- /dev/null
+++ b/frontend/src/components/svgIcons/clipboard.tsx
@@ -0,0 +1,14 @@
+
+// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
+
+import { HTMLProps } from "react";
+
+// License: CC-By 4.0
+export const ClipboardIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/clock.js b/frontend/src/components/svgIcons/clock.js
deleted file mode 100644
index 9d0459913e..0000000000
--- a/frontend/src/components/svgIcons/clock.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { PureComponent } from 'react';
-
-export class ClockIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/clock.tsx b/frontend/src/components/svgIcons/clock.tsx
new file mode 100644
index 0000000000..23ea89f1fe
--- /dev/null
+++ b/frontend/src/components/svgIcons/clock.tsx
@@ -0,0 +1,14 @@
+import { HTMLProps } from "react";
+
+export const ClockIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/close.js b/frontend/src/components/svgIcons/close.js
deleted file mode 100644
index d7122d4879..0000000000
--- a/frontend/src/components/svgIcons/close.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import { PureComponent } from 'react';
-
-export class CloseIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/close.tsx b/frontend/src/components/svgIcons/close.tsx
new file mode 100644
index 0000000000..4732976c7e
--- /dev/null
+++ b/frontend/src/components/svgIcons/close.tsx
@@ -0,0 +1,10 @@
+import { HTMLProps } from "react";
+
+export const CloseIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/comment.js b/frontend/src/components/svgIcons/comment.js
deleted file mode 100644
index 38ce263370..0000000000
--- a/frontend/src/components/svgIcons/comment.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { PureComponent } from 'react';
-
-// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
-// License: CC-By 4.0
-export class CommentIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/comment.tsx b/frontend/src/components/svgIcons/comment.tsx
new file mode 100644
index 0000000000..35ccf9edf1
--- /dev/null
+++ b/frontend/src/components/svgIcons/comment.tsx
@@ -0,0 +1,12 @@
+import { HTMLProps } from "react";
+
+// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
+// License: CC-By 4.0
+export const CommentIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/copyright.js b/frontend/src/components/svgIcons/copyright.js
deleted file mode 100644
index 2ac5918e1f..0000000000
--- a/frontend/src/components/svgIcons/copyright.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { PureComponent } from 'react';
-
-// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
-// License: CC-By 4.0
-export class CopyrightIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/copyright.tsx b/frontend/src/components/svgIcons/copyright.tsx
new file mode 100644
index 0000000000..8bcd0a12f9
--- /dev/null
+++ b/frontend/src/components/svgIcons/copyright.tsx
@@ -0,0 +1,12 @@
+import { HTMLProps } from 'react';
+
+// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
+// License: CC-By 4.0
+export const CopyrightIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/cut.js b/frontend/src/components/svgIcons/cut.js
deleted file mode 100644
index 76b169340a..0000000000
--- a/frontend/src/components/svgIcons/cut.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { PureComponent } from 'react';
-
-// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
-// License: CC-By 4.0
-export class CutIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/cut.tsx b/frontend/src/components/svgIcons/cut.tsx
new file mode 100644
index 0000000000..5acb011131
--- /dev/null
+++ b/frontend/src/components/svgIcons/cut.tsx
@@ -0,0 +1,12 @@
+import { HTMLProps } from "react";
+
+// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
+// License: CC-By 4.0
+export const CutIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/dataUse.js b/frontend/src/components/svgIcons/dataUse.js
deleted file mode 100644
index 791ec18e4e..0000000000
--- a/frontend/src/components/svgIcons/dataUse.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import { PureComponent } from 'react';
-
-export class DataUseIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/dataUse.tsx b/frontend/src/components/svgIcons/dataUse.tsx
new file mode 100644
index 0000000000..5831072550
--- /dev/null
+++ b/frontend/src/components/svgIcons/dataUse.tsx
@@ -0,0 +1,20 @@
+import { HTMLProps } from "react";
+
+export const DataUseIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/disasterResponse.js b/frontend/src/components/svgIcons/disasterResponse.js
deleted file mode 100644
index 7b0d141c03..0000000000
--- a/frontend/src/components/svgIcons/disasterResponse.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import { PureComponent } from 'react';
-
-export class DisasterResponseIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/disasterResponse.tsx b/frontend/src/components/svgIcons/disasterResponse.tsx
new file mode 100644
index 0000000000..d851534536
--- /dev/null
+++ b/frontend/src/components/svgIcons/disasterResponse.tsx
@@ -0,0 +1,29 @@
+import { HTMLProps } from "react";
+
+export const DisasterResponseIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/download.js b/frontend/src/components/svgIcons/download.js
deleted file mode 100644
index 61d0399042..0000000000
--- a/frontend/src/components/svgIcons/download.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import React from 'react';
-
-export class DownloadIcon extends React.PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/download.tsx b/frontend/src/components/svgIcons/download.tsx
new file mode 100644
index 0000000000..de1b76d74d
--- /dev/null
+++ b/frontend/src/components/svgIcons/download.tsx
@@ -0,0 +1,17 @@
+import { HTMLProps } from "react";
+
+export const DownloadIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/edit.js b/frontend/src/components/svgIcons/edit.js
deleted file mode 100644
index 1c8b41e1c9..0000000000
--- a/frontend/src/components/svgIcons/edit.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { PureComponent } from 'react';
-
-// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
-// License: CC-By 4.0
-export class EditIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/edit.tsx b/frontend/src/components/svgIcons/edit.tsx
new file mode 100644
index 0000000000..6d7e1f7c3c
--- /dev/null
+++ b/frontend/src/components/svgIcons/edit.tsx
@@ -0,0 +1,12 @@
+import { HTMLProps } from "react";
+
+// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
+// License: CC-By 4.0
+export const EditIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/envelope.js b/frontend/src/components/svgIcons/envelope.js
deleted file mode 100644
index 0772d4b0ae..0000000000
--- a/frontend/src/components/svgIcons/envelope.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { PureComponent } from 'react';
-
-// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
-// License: CC-By 4.0
-export class EnvelopeIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/envelope.tsx b/frontend/src/components/svgIcons/envelope.tsx
new file mode 100644
index 0000000000..a85446cbc9
--- /dev/null
+++ b/frontend/src/components/svgIcons/envelope.tsx
@@ -0,0 +1,12 @@
+import { HTMLProps } from "react";
+
+// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
+// License: CC-By 4.0
+export const EnvelopeIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/exit.js b/frontend/src/components/svgIcons/exit.js
deleted file mode 100644
index 7896c8cc8a..0000000000
--- a/frontend/src/components/svgIcons/exit.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import { PureComponent } from 'react';
-
-export class ExitIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/exit.tsx b/frontend/src/components/svgIcons/exit.tsx
new file mode 100644
index 0000000000..bd3207a647
--- /dev/null
+++ b/frontend/src/components/svgIcons/exit.tsx
@@ -0,0 +1,10 @@
+import { HTMLProps } from "react";
+
+export const ExitIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/eye.js b/frontend/src/components/svgIcons/eye.js
deleted file mode 100644
index a3ffb40456..0000000000
--- a/frontend/src/components/svgIcons/eye.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import { PureComponent } from 'react';
-
-export class EyeIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/eye.tsx b/frontend/src/components/svgIcons/eye.tsx
new file mode 100644
index 0000000000..078a099e8d
--- /dev/null
+++ b/frontend/src/components/svgIcons/eye.tsx
@@ -0,0 +1,11 @@
+import { HTMLProps } from "react";
+
+export const EyeIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/facebook.js b/frontend/src/components/svgIcons/facebook.js
deleted file mode 100644
index c5be8edb37..0000000000
--- a/frontend/src/components/svgIcons/facebook.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { PureComponent } from 'react';
-
-export class FacebookIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/facebook.tsx b/frontend/src/components/svgIcons/facebook.tsx
new file mode 100644
index 0000000000..5b21b21a33
--- /dev/null
+++ b/frontend/src/components/svgIcons/facebook.tsx
@@ -0,0 +1,14 @@
+import { HTMLProps } from "react";
+
+export const FacebookIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/fileImport.js b/frontend/src/components/svgIcons/fileImport.js
deleted file mode 100644
index bb4413e205..0000000000
--- a/frontend/src/components/svgIcons/fileImport.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { PureComponent } from 'react';
-
-// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
-// License: CC-By 4.0
-export class FileImportIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/fileImport.tsx b/frontend/src/components/svgIcons/fileImport.tsx
new file mode 100644
index 0000000000..684ebeb6a1
--- /dev/null
+++ b/frontend/src/components/svgIcons/fileImport.tsx
@@ -0,0 +1,12 @@
+import { HTMLProps } from "react";
+
+// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
+// License: CC-By 4.0
+export const FileImportIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/flag.js b/frontend/src/components/svgIcons/flag.js
deleted file mode 100644
index bd99a94af7..0000000000
--- a/frontend/src/components/svgIcons/flag.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { PureComponent } from 'react';
-
-export class FlagIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/flag.tsx b/frontend/src/components/svgIcons/flag.tsx
new file mode 100644
index 0000000000..fa44754a97
--- /dev/null
+++ b/frontend/src/components/svgIcons/flag.tsx
@@ -0,0 +1,14 @@
+import { HTMLProps } from "react";
+
+export const FlagIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/github.js b/frontend/src/components/svgIcons/github.js
deleted file mode 100644
index 414e6405ca..0000000000
--- a/frontend/src/components/svgIcons/github.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { PureComponent } from 'react';
-
-export class GithubIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/github.tsx b/frontend/src/components/svgIcons/github.tsx
new file mode 100644
index 0000000000..378fdd3aa9
--- /dev/null
+++ b/frontend/src/components/svgIcons/github.tsx
@@ -0,0 +1,14 @@
+import { HTMLProps } from "react";
+
+export const GithubIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/grid.js b/frontend/src/components/svgIcons/grid.js
deleted file mode 100644
index d22e1324bb..0000000000
--- a/frontend/src/components/svgIcons/grid.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import { PureComponent } from 'react';
-
-// Icons produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
-// License: CC-By 4.0
-
-export class FourCellsGridIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
-
-export class NineCellsGridIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/grid.tsx b/frontend/src/components/svgIcons/grid.tsx
new file mode 100644
index 0000000000..693f81ebd5
--- /dev/null
+++ b/frontend/src/components/svgIcons/grid.tsx
@@ -0,0 +1,22 @@
+import { HTMLProps } from "react";
+
+// Icons produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
+// License: CC-By 4.0
+export const FourCellsGridIcon = (props: HTMLProps) => (
+
+
+
+);
+
+export const NineCellsGridIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/grip.js b/frontend/src/components/svgIcons/grip.js
deleted file mode 100644
index 18b8784f3b..0000000000
--- a/frontend/src/components/svgIcons/grip.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { PureComponent } from 'react';
-
-// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
-// License: CC-By 4.0
-export class GripIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/grip.tsx b/frontend/src/components/svgIcons/grip.tsx
new file mode 100644
index 0000000000..b114edc1ad
--- /dev/null
+++ b/frontend/src/components/svgIcons/grip.tsx
@@ -0,0 +1,12 @@
+import { HTMLProps } from "react";
+
+// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
+// License: CC-By 4.0
+export const GripIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/hashtag.js b/frontend/src/components/svgIcons/hashtag.js
deleted file mode 100644
index 60dd5710b6..0000000000
--- a/frontend/src/components/svgIcons/hashtag.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { PureComponent } from 'react';
-
-// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
-// License: CC-By 4.0
-export class HashtagIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/hashtag.tsx b/frontend/src/components/svgIcons/hashtag.tsx
new file mode 100644
index 0000000000..87914e2275
--- /dev/null
+++ b/frontend/src/components/svgIcons/hashtag.tsx
@@ -0,0 +1,12 @@
+import { HTMLProps } from "react";
+
+// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
+// License: CC-By 4.0
+export const HashtagIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/health.js b/frontend/src/components/svgIcons/health.js
deleted file mode 100644
index f213211a98..0000000000
--- a/frontend/src/components/svgIcons/health.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import { PureComponent } from 'react';
-
-export class HealthIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/health.tsx b/frontend/src/components/svgIcons/health.tsx
new file mode 100644
index 0000000000..17e6b73721
--- /dev/null
+++ b/frontend/src/components/svgIcons/health.tsx
@@ -0,0 +1,22 @@
+import { HTMLProps } from "react";
+
+export const HealthIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/home.js b/frontend/src/components/svgIcons/home.js
deleted file mode 100644
index 1518aa9c72..0000000000
--- a/frontend/src/components/svgIcons/home.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { PureComponent } from 'react';
-
-export class HomeIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/home.tsx b/frontend/src/components/svgIcons/home.tsx
new file mode 100644
index 0000000000..a3a6ae8a28
--- /dev/null
+++ b/frontend/src/components/svgIcons/home.tsx
@@ -0,0 +1,14 @@
+import { HTMLProps } from "react";
+
+export const HomeIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/humanProcessing.js b/frontend/src/components/svgIcons/humanProcessing.js
deleted file mode 100644
index 8d3fb26799..0000000000
--- a/frontend/src/components/svgIcons/humanProcessing.js
+++ /dev/null
@@ -1,58 +0,0 @@
-import { PureComponent } from 'react';
-
-export class HumanProcessingIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/humanProcessing.tsx b/frontend/src/components/svgIcons/humanProcessing.tsx
new file mode 100644
index 0000000000..be9e2286bb
--- /dev/null
+++ b/frontend/src/components/svgIcons/humanProcessing.tsx
@@ -0,0 +1,54 @@
+import { HTMLProps } from 'react';
+
+export const HumanProcessingIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/index.js b/frontend/src/components/svgIcons/index.ts
similarity index 100%
rename from frontend/src/components/svgIcons/index.js
rename to frontend/src/components/svgIcons/index.ts
diff --git a/frontend/src/components/svgIcons/info.js b/frontend/src/components/svgIcons/info.js
deleted file mode 100644
index 7853450b73..0000000000
--- a/frontend/src/components/svgIcons/info.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import { PureComponent } from 'react';
-
-export class InfoIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/info.tsx b/frontend/src/components/svgIcons/info.tsx
new file mode 100644
index 0000000000..98aa62e388
--- /dev/null
+++ b/frontend/src/components/svgIcons/info.tsx
@@ -0,0 +1,10 @@
+import { HTMLProps } from 'react';
+
+export const InfoIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/instagram.js b/frontend/src/components/svgIcons/instagram.js
deleted file mode 100644
index 1cf654f5b4..0000000000
--- a/frontend/src/components/svgIcons/instagram.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import { PureComponent } from 'react';
-
-export class InstagramIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/instagram.tsx b/frontend/src/components/svgIcons/instagram.tsx
new file mode 100644
index 0000000000..5c8907c077
--- /dev/null
+++ b/frontend/src/components/svgIcons/instagram.tsx
@@ -0,0 +1,19 @@
+import { HTMLProps } from 'react';
+
+export const InstagramIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/invalidated.js b/frontend/src/components/svgIcons/invalidated.js
deleted file mode 100644
index e76992c8eb..0000000000
--- a/frontend/src/components/svgIcons/invalidated.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import { PureComponent } from 'react';
-
-export class InvalidatedIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/invalidated.tsx b/frontend/src/components/svgIcons/invalidated.tsx
new file mode 100644
index 0000000000..1ece405ef1
--- /dev/null
+++ b/frontend/src/components/svgIcons/invalidated.tsx
@@ -0,0 +1,13 @@
+import { HTMLProps } from 'react';
+
+export const InvalidatedIcon = (props: HTMLProps) => (
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/left.js b/frontend/src/components/svgIcons/left.js
deleted file mode 100644
index f15c72fce7..0000000000
--- a/frontend/src/components/svgIcons/left.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { PureComponent } from 'react';
-
-export class LeftIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/left.tsx b/frontend/src/components/svgIcons/left.tsx
new file mode 100644
index 0000000000..11abb93e2d
--- /dev/null
+++ b/frontend/src/components/svgIcons/left.tsx
@@ -0,0 +1,14 @@
+import { HTMLProps } from "react";
+
+export const LeftIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/link.js b/frontend/src/components/svgIcons/link.js
deleted file mode 100644
index 070059d3e9..0000000000
--- a/frontend/src/components/svgIcons/link.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import { PureComponent } from 'react';
-
-export class ExternalLinkIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
-
-export class InternalLinkIcon extends PureComponent {
- // Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
- // License: CC-By 4.0
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/link.tsx b/frontend/src/components/svgIcons/link.tsx
new file mode 100644
index 0000000000..c51dd1d5d9
--- /dev/null
+++ b/frontend/src/components/svgIcons/link.tsx
@@ -0,0 +1,21 @@
+import { HTMLProps } from "react";
+
+export const ExternalLinkIcon = (props: HTMLProps) => (
+
+
+
+);
+
+export const InternalLinkIcon = (props: HTMLProps) => (
+ // Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
+ // License: CC-By 4.0
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/linkedin.js b/frontend/src/components/svgIcons/linkedin.js
deleted file mode 100644
index dab9b14021..0000000000
--- a/frontend/src/components/svgIcons/linkedin.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { PureComponent } from 'react';
-
-// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
-// License: CC-By 4.0
-export class LinkedinIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/linkedin.tsx b/frontend/src/components/svgIcons/linkedin.tsx
new file mode 100644
index 0000000000..dd4a1c1976
--- /dev/null
+++ b/frontend/src/components/svgIcons/linkedin.tsx
@@ -0,0 +1,12 @@
+import { HTMLProps } from 'react';
+
+// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
+// License: CC-By 4.0
+export const LinkedinIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/list.js b/frontend/src/components/svgIcons/list.js
deleted file mode 100644
index c021484125..0000000000
--- a/frontend/src/components/svgIcons/list.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { PureComponent } from 'react';
-
-export class ListIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/list.tsx b/frontend/src/components/svgIcons/list.tsx
new file mode 100644
index 0000000000..67c58b6db8
--- /dev/null
+++ b/frontend/src/components/svgIcons/list.tsx
@@ -0,0 +1,14 @@
+import { HTMLProps } from 'react';
+
+export const ListIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/loading.js b/frontend/src/components/svgIcons/loading.js
deleted file mode 100644
index 3a28a9afae..0000000000
--- a/frontend/src/components/svgIcons/loading.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import { PureComponent } from 'react';
-
-export class LoadingIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/loading.tsx b/frontend/src/components/svgIcons/loading.tsx
new file mode 100644
index 0000000000..a9d22644bf
--- /dev/null
+++ b/frontend/src/components/svgIcons/loading.tsx
@@ -0,0 +1,10 @@
+import { HTMLProps } from 'react';
+
+export const LoadingIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/lock.js b/frontend/src/components/svgIcons/lock.js
deleted file mode 100644
index 4a531a0c1b..0000000000
--- a/frontend/src/components/svgIcons/lock.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import { PureComponent } from 'react';
-
-export class LockIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/lock.tsx b/frontend/src/components/svgIcons/lock.tsx
new file mode 100644
index 0000000000..5f4d717cee
--- /dev/null
+++ b/frontend/src/components/svgIcons/lock.tsx
@@ -0,0 +1,11 @@
+import { HTMLProps } from "react";
+
+export const LockIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/manage.js b/frontend/src/components/svgIcons/manage.js
deleted file mode 100644
index be01874822..0000000000
--- a/frontend/src/components/svgIcons/manage.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import { PureComponent } from 'react';
-
-export class ManageIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
-
-
-
- );
- }
-}
-
-export class GearIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/manage.tsx b/frontend/src/components/svgIcons/manage.tsx
new file mode 100644
index 0000000000..910f9a6373
--- /dev/null
+++ b/frontend/src/components/svgIcons/manage.tsx
@@ -0,0 +1,38 @@
+import { HTMLProps } from 'react';
+
+export const ManageIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+
+
+
+);
+
+export const GearIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/map.js b/frontend/src/components/svgIcons/map.js
deleted file mode 100644
index dfd1bdb1bc..0000000000
--- a/frontend/src/components/svgIcons/map.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { PureComponent } from 'react';
-
-// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
-// License: CC-By 4.0
-export class MapIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/map.tsx b/frontend/src/components/svgIcons/map.tsx
new file mode 100644
index 0000000000..3f1424d69d
--- /dev/null
+++ b/frontend/src/components/svgIcons/map.tsx
@@ -0,0 +1,12 @@
+import { HTMLProps } from 'react';
+
+// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
+// License: CC-By 4.0
+export const MapIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/mapped.js b/frontend/src/components/svgIcons/mapped.js
deleted file mode 100644
index 0177ac226d..0000000000
--- a/frontend/src/components/svgIcons/mapped.js
+++ /dev/null
@@ -1,52 +0,0 @@
-import { PureComponent } from 'react';
-
-export class MappedIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/mapped.tsx b/frontend/src/components/svgIcons/mapped.tsx
new file mode 100644
index 0000000000..ad9386ca4f
--- /dev/null
+++ b/frontend/src/components/svgIcons/mapped.tsx
@@ -0,0 +1,48 @@
+import { HTMLProps } from 'react';
+
+export const MappedIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/mappedSquare.js b/frontend/src/components/svgIcons/mappedSquare.js
deleted file mode 100644
index f9e060e1ad..0000000000
--- a/frontend/src/components/svgIcons/mappedSquare.js
+++ /dev/null
@@ -1,73 +0,0 @@
-import { PureComponent } from 'react';
-
-export class MappedSquareIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/mappedSquare.tsx b/frontend/src/components/svgIcons/mappedSquare.tsx
new file mode 100644
index 0000000000..5d6bbfacbc
--- /dev/null
+++ b/frontend/src/components/svgIcons/mappedSquare.tsx
@@ -0,0 +1,69 @@
+import { HTMLProps } from 'react';
+
+export const MappedSquareIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/mapping.js b/frontend/src/components/svgIcons/mapping.js
deleted file mode 100644
index 1a24961ac7..0000000000
--- a/frontend/src/components/svgIcons/mapping.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import { PureComponent } from 'react';
-
-export class MappingIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/mapping.tsx b/frontend/src/components/svgIcons/mapping.tsx
new file mode 100644
index 0000000000..b8b3fe9608
--- /dev/null
+++ b/frontend/src/components/svgIcons/mapping.tsx
@@ -0,0 +1,27 @@
+import { HTMLProps } from 'react';
+
+export const MappingIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/marker.js b/frontend/src/components/svgIcons/marker.js
deleted file mode 100644
index 7df91944a6..0000000000
--- a/frontend/src/components/svgIcons/marker.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import { PureComponent } from 'react';
-
-export class MarkerIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/marker.tsx b/frontend/src/components/svgIcons/marker.tsx
new file mode 100644
index 0000000000..c462463665
--- /dev/null
+++ b/frontend/src/components/svgIcons/marker.tsx
@@ -0,0 +1,10 @@
+import { HTMLProps } from 'react';
+
+export const MarkerIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/menu.js b/frontend/src/components/svgIcons/menu.js
deleted file mode 100644
index 821f584d17..0000000000
--- a/frontend/src/components/svgIcons/menu.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import { PureComponent } from 'react';
-
-export class MenuIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/menu.tsx b/frontend/src/components/svgIcons/menu.tsx
new file mode 100644
index 0000000000..4d7f6bd0be
--- /dev/null
+++ b/frontend/src/components/svgIcons/menu.tsx
@@ -0,0 +1,11 @@
+import { HTMLProps } from 'react';
+
+export const MenuIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/pencil.js b/frontend/src/components/svgIcons/pencil.js
deleted file mode 100644
index 5357ba5dc1..0000000000
--- a/frontend/src/components/svgIcons/pencil.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import { PureComponent } from 'react';
-
-export class PencilIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/pencil.tsx b/frontend/src/components/svgIcons/pencil.tsx
new file mode 100644
index 0000000000..65a3875eb2
--- /dev/null
+++ b/frontend/src/components/svgIcons/pencil.tsx
@@ -0,0 +1,11 @@
+import { HTMLProps } from 'react';
+
+export const PencilIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/play.js b/frontend/src/components/svgIcons/play.js
deleted file mode 100644
index 46ed3700ff..0000000000
--- a/frontend/src/components/svgIcons/play.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import { PureComponent } from 'react';
-
-export class PlayIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/play.tsx b/frontend/src/components/svgIcons/play.tsx
new file mode 100644
index 0000000000..f0bb13c03f
--- /dev/null
+++ b/frontend/src/components/svgIcons/play.tsx
@@ -0,0 +1,10 @@
+import { HTMLProps } from "react";
+
+export const PlayIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/plus.js b/frontend/src/components/svgIcons/plus.js
deleted file mode 100644
index a0176a707f..0000000000
--- a/frontend/src/components/svgIcons/plus.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { PureComponent } from 'react';
-
-// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
-// License: CC-By 4.0
-export class PlusIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/plus.tsx b/frontend/src/components/svgIcons/plus.tsx
new file mode 100644
index 0000000000..0333d35f57
--- /dev/null
+++ b/frontend/src/components/svgIcons/plus.tsx
@@ -0,0 +1,12 @@
+import { HTMLProps } from 'react';
+
+// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
+// License: CC-By 4.0
+export const PlusIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/polygon.js b/frontend/src/components/svgIcons/polygon.js
deleted file mode 100644
index f6877e5b29..0000000000
--- a/frontend/src/components/svgIcons/polygon.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import { PureComponent } from 'react';
-
-export class PolygonIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/polygon.tsx b/frontend/src/components/svgIcons/polygon.tsx
new file mode 100644
index 0000000000..c32e4d7612
--- /dev/null
+++ b/frontend/src/components/svgIcons/polygon.tsx
@@ -0,0 +1,50 @@
+import { HTMLProps } from 'react';
+
+export const PolygonIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/profilePicture.js b/frontend/src/components/svgIcons/profilePicture.js
deleted file mode 100644
index 9b29941f20..0000000000
--- a/frontend/src/components/svgIcons/profilePicture.js
+++ /dev/null
@@ -1,71 +0,0 @@
-import { PureComponent } from 'react';
-
-export class ProfilePictureIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
- );
- }
-}
-
-export class UserIcon extends PureComponent {
- render() {
- return (
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/profilePicture.tsx b/frontend/src/components/svgIcons/profilePicture.tsx
new file mode 100644
index 0000000000..47e50ef85e
--- /dev/null
+++ b/frontend/src/components/svgIcons/profilePicture.tsx
@@ -0,0 +1,63 @@
+import { HTMLProps } from 'react';
+
+export const ProfilePictureIcon = (props: HTMLProps) => (
+
+
+
+
+
+);
+
+export const UserIcon = (props: HTMLProps) => (
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/projectSelection.js b/frontend/src/components/svgIcons/projectSelection.js
deleted file mode 100644
index 6b0efc9f96..0000000000
--- a/frontend/src/components/svgIcons/projectSelection.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import { PureComponent } from 'react';
-
-export class ProjectSelectionIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/projectSelection.tsx b/frontend/src/components/svgIcons/projectSelection.tsx
new file mode 100644
index 0000000000..2318868c65
--- /dev/null
+++ b/frontend/src/components/svgIcons/projectSelection.tsx
@@ -0,0 +1,18 @@
+import { HTMLProps } from 'react';
+
+export const ProjectSelectionIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/questionCircle.js b/frontend/src/components/svgIcons/questionCircle.js
deleted file mode 100644
index a775cb612c..0000000000
--- a/frontend/src/components/svgIcons/questionCircle.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { PureComponent } from 'react';
-
-// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
-// License: CC-By 4.0
-export class QuestionCircleIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/questionCircle.tsx b/frontend/src/components/svgIcons/questionCircle.tsx
new file mode 100644
index 0000000000..ead29557d5
--- /dev/null
+++ b/frontend/src/components/svgIcons/questionCircle.tsx
@@ -0,0 +1,12 @@
+import { HTMLProps } from 'react';
+
+// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
+// License: CC-By 4.0
+export const QuestionCircleIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/refresh.js b/frontend/src/components/svgIcons/refresh.js
deleted file mode 100644
index 3e35b8b9b0..0000000000
--- a/frontend/src/components/svgIcons/refresh.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { PureComponent } from 'react';
-
-// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
-// License: CC-By 4.0
-export class RefreshIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/refresh.tsx b/frontend/src/components/svgIcons/refresh.tsx
new file mode 100644
index 0000000000..74bc9ba443
--- /dev/null
+++ b/frontend/src/components/svgIcons/refresh.tsx
@@ -0,0 +1,12 @@
+import { HTMLProps } from 'react';
+
+// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
+// License: CC-By 4.0
+export const RefreshIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/refugeeResponse.js b/frontend/src/components/svgIcons/refugeeResponse.js
deleted file mode 100644
index 1167f6c562..0000000000
--- a/frontend/src/components/svgIcons/refugeeResponse.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import { PureComponent } from 'react';
-
-export class RefugeeResponseIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/refugeeResponse.tsx b/frontend/src/components/svgIcons/refugeeResponse.tsx
new file mode 100644
index 0000000000..9db5682e66
--- /dev/null
+++ b/frontend/src/components/svgIcons/refugeeResponse.tsx
@@ -0,0 +1,27 @@
+import { HTMLProps } from 'react';
+
+export const RefugeeResponseIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/resume.js b/frontend/src/components/svgIcons/resume.js
deleted file mode 100644
index 77be64bbbc..0000000000
--- a/frontend/src/components/svgIcons/resume.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { PureComponent } from 'react';
-
-export class ResumeIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/resume.tsx b/frontend/src/components/svgIcons/resume.tsx
new file mode 100644
index 0000000000..732108cd40
--- /dev/null
+++ b/frontend/src/components/svgIcons/resume.tsx
@@ -0,0 +1,14 @@
+import { HTMLProps } from "react";
+
+export const ResumeIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/right.js b/frontend/src/components/svgIcons/right.js
deleted file mode 100644
index cd53ed9917..0000000000
--- a/frontend/src/components/svgIcons/right.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { PureComponent } from 'react';
-
-export class RightIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/right.tsx b/frontend/src/components/svgIcons/right.tsx
new file mode 100644
index 0000000000..fb6420908d
--- /dev/null
+++ b/frontend/src/components/svgIcons/right.tsx
@@ -0,0 +1,14 @@
+import { HTMLProps } from "react";
+
+export const RightIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/road.js b/frontend/src/components/svgIcons/road.js
deleted file mode 100644
index c403900a23..0000000000
--- a/frontend/src/components/svgIcons/road.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import { PureComponent } from 'react';
-
-export class RoadIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/road.tsx b/frontend/src/components/svgIcons/road.tsx
new file mode 100644
index 0000000000..00f8a6d9ed
--- /dev/null
+++ b/frontend/src/components/svgIcons/road.tsx
@@ -0,0 +1,19 @@
+import { HTMLProps } from "react";
+
+export const RoadIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/search.js b/frontend/src/components/svgIcons/search.js
deleted file mode 100644
index d46b8d87db..0000000000
--- a/frontend/src/components/svgIcons/search.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { PureComponent } from 'react';
-
-export class SearchIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/search.tsx b/frontend/src/components/svgIcons/search.tsx
new file mode 100644
index 0000000000..3d49066a29
--- /dev/null
+++ b/frontend/src/components/svgIcons/search.tsx
@@ -0,0 +1,14 @@
+import { HTMLProps } from 'react';
+
+export const SearchIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/selectProject.js b/frontend/src/components/svgIcons/selectProject.js
deleted file mode 100644
index 9e56c87593..0000000000
--- a/frontend/src/components/svgIcons/selectProject.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import { PureComponent } from 'react';
-
-export class SelectProject extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/selectProject.tsx b/frontend/src/components/svgIcons/selectProject.tsx
new file mode 100644
index 0000000000..ece993adde
--- /dev/null
+++ b/frontend/src/components/svgIcons/selectProject.tsx
@@ -0,0 +1,27 @@
+import { HTMLProps } from "react";
+
+export const SelectProject = (props: HTMLProps) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/selectTask.js b/frontend/src/components/svgIcons/selectTask.js
deleted file mode 100644
index 5703bda722..0000000000
--- a/frontend/src/components/svgIcons/selectTask.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import { PureComponent } from 'react';
-
-export class SelectTask extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/selectTask.tsx b/frontend/src/components/svgIcons/selectTask.tsx
new file mode 100644
index 0000000000..15ed36789c
--- /dev/null
+++ b/frontend/src/components/svgIcons/selectTask.tsx
@@ -0,0 +1,29 @@
+import { HTMLProps } from "react";
+
+export const SelectTask = (props: HTMLProps) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/settings.js b/frontend/src/components/svgIcons/settings.js
deleted file mode 100644
index 326deb65ff..0000000000
--- a/frontend/src/components/svgIcons/settings.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import { PureComponent } from 'react';
-
-export class SettingsIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/settings.tsx b/frontend/src/components/svgIcons/settings.tsx
new file mode 100644
index 0000000000..a1aaebdd5d
--- /dev/null
+++ b/frontend/src/components/svgIcons/settings.tsx
@@ -0,0 +1,11 @@
+import { HTMLProps } from "react";
+
+export const SettingsIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/share.js b/frontend/src/components/svgIcons/share.js
deleted file mode 100644
index d6a2da83d5..0000000000
--- a/frontend/src/components/svgIcons/share.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { PureComponent } from 'react';
-
-export class ShareIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/share.tsx b/frontend/src/components/svgIcons/share.tsx
new file mode 100644
index 0000000000..dfa8ef92ec
--- /dev/null
+++ b/frontend/src/components/svgIcons/share.tsx
@@ -0,0 +1,14 @@
+import { HTMLProps } from "react";
+
+export const ShareIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/sidebar.js b/frontend/src/components/svgIcons/sidebar.js
deleted file mode 100644
index 0a911f0eef..0000000000
--- a/frontend/src/components/svgIcons/sidebar.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import { PureComponent } from 'react';
-
-export class SidebarIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/sidebar.tsx b/frontend/src/components/svgIcons/sidebar.tsx
new file mode 100644
index 0000000000..e7fbedd105
--- /dev/null
+++ b/frontend/src/components/svgIcons/sidebar.tsx
@@ -0,0 +1,10 @@
+import { HTMLProps } from 'react';
+
+export const SidebarIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/star.js b/frontend/src/components/svgIcons/star.js
deleted file mode 100644
index 4c68e128cd..0000000000
--- a/frontend/src/components/svgIcons/star.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import { PureComponent } from 'react';
-
-// Icons produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
-// License: CC-By 4.0
-export class FullStarIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
-
-export class StarIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
-
-export class HalfStarIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/star.tsx b/frontend/src/components/svgIcons/star.tsx
new file mode 100644
index 0000000000..cca215e878
--- /dev/null
+++ b/frontend/src/components/svgIcons/star.tsx
@@ -0,0 +1,30 @@
+import { HTMLProps } from "react";
+
+// Icons produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
+// License: CC-By 4.0
+export const FullStarIcon = (props: HTMLProps) => (
+
+
+
+);
+
+export const StarIcon = (props: HTMLProps) => (
+
+
+
+);
+
+export const HalfStarIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/submitWork.js b/frontend/src/components/svgIcons/submitWork.js
deleted file mode 100644
index ab94858ea5..0000000000
--- a/frontend/src/components/svgIcons/submitWork.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import { PureComponent } from 'react';
-
-export class SubmitWorkIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/submitWork.tsx b/frontend/src/components/svgIcons/submitWork.tsx
new file mode 100644
index 0000000000..7d065ba20d
--- /dev/null
+++ b/frontend/src/components/svgIcons/submitWork.tsx
@@ -0,0 +1,17 @@
+import { HTMLProps } from 'react';
+
+export const SubmitWorkIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/task.js b/frontend/src/components/svgIcons/task.js
deleted file mode 100644
index 865cdd8b83..0000000000
--- a/frontend/src/components/svgIcons/task.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import { PureComponent } from 'react';
-
-export class TaskIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/task.tsx b/frontend/src/components/svgIcons/task.tsx
new file mode 100644
index 0000000000..6933da91b9
--- /dev/null
+++ b/frontend/src/components/svgIcons/task.tsx
@@ -0,0 +1,13 @@
+import { HTMLProps } from "react";
+
+export const TaskIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/taskSelection.js b/frontend/src/components/svgIcons/taskSelection.js
deleted file mode 100644
index 833af1d386..0000000000
--- a/frontend/src/components/svgIcons/taskSelection.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import { PureComponent } from 'react';
-
-export class TaskSelectionIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/taskSelection.tsx b/frontend/src/components/svgIcons/taskSelection.tsx
new file mode 100644
index 0000000000..e5c15942e7
--- /dev/null
+++ b/frontend/src/components/svgIcons/taskSelection.tsx
@@ -0,0 +1,21 @@
+import { HTMLProps } from 'react';
+
+export const TaskSelectionIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/timer.js b/frontend/src/components/svgIcons/timer.js
deleted file mode 100644
index 517d368adf..0000000000
--- a/frontend/src/components/svgIcons/timer.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import { PureComponent } from 'react';
-
-export class TimerIcon extends PureComponent {
- render() {
- return (
-
- Timer
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/timer.tsx b/frontend/src/components/svgIcons/timer.tsx
new file mode 100644
index 0000000000..5f02fb957c
--- /dev/null
+++ b/frontend/src/components/svgIcons/timer.tsx
@@ -0,0 +1,21 @@
+import { HTMLProps } from 'react';
+
+export const TimerIcon = (props: HTMLProps) => (
+
+ Timer
+
+
+);
diff --git a/frontend/src/components/svgIcons/twitter.js b/frontend/src/components/svgIcons/twitter.js
deleted file mode 100644
index 19ff3efaf1..0000000000
--- a/frontend/src/components/svgIcons/twitter.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import { PureComponent } from 'react';
-
-export class TwitterIcon extends PureComponent {
- render() {
- return (
-
- {!this.props.noBg && }
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/twitter.tsx b/frontend/src/components/svgIcons/twitter.tsx
new file mode 100644
index 0000000000..49086e50a4
--- /dev/null
+++ b/frontend/src/components/svgIcons/twitter.tsx
@@ -0,0 +1,25 @@
+import { HTMLProps } from 'react';
+
+export const TwitterIcon = (props: HTMLProps & {
+ noBg?: boolean;
+}) => {
+ const { noBg, ...rest } = props;
+
+ return (
+
+ {!props.noBg && }
+
+
+ )
+}
diff --git a/frontend/src/components/svgIcons/undo.js b/frontend/src/components/svgIcons/undo.js
deleted file mode 100644
index ee52ad6890..0000000000
--- a/frontend/src/components/svgIcons/undo.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { PureComponent } from 'react';
-
-// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
-// License: CC-By 4.0
-export class UndoIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/undo.tsx b/frontend/src/components/svgIcons/undo.tsx
new file mode 100644
index 0000000000..5cc7a45c20
--- /dev/null
+++ b/frontend/src/components/svgIcons/undo.tsx
@@ -0,0 +1,12 @@
+import { HTMLProps } from 'react';
+
+// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
+// License: CC-By 4.0
+export const UndoIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/validated.js b/frontend/src/components/svgIcons/validated.js
deleted file mode 100644
index bafb4964cf..0000000000
--- a/frontend/src/components/svgIcons/validated.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import { PureComponent } from 'react';
-
-export class ValidatedIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/validated.tsx b/frontend/src/components/svgIcons/validated.tsx
new file mode 100644
index 0000000000..c35e297483
--- /dev/null
+++ b/frontend/src/components/svgIcons/validated.tsx
@@ -0,0 +1,13 @@
+import { HTMLProps } from 'react';
+
+export const ValidatedIcon = (props: HTMLProps) => (
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/validation.js b/frontend/src/components/svgIcons/validation.js
deleted file mode 100644
index 7d3312ed01..0000000000
--- a/frontend/src/components/svgIcons/validation.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import { PureComponent } from 'react';
-
-export class ValidationIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/validation.tsx b/frontend/src/components/svgIcons/validation.tsx
new file mode 100644
index 0000000000..7932ed92ce
--- /dev/null
+++ b/frontend/src/components/svgIcons/validation.tsx
@@ -0,0 +1,28 @@
+import { HTMLProps } from 'react';
+
+export const ValidationIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/view.js b/frontend/src/components/svgIcons/view.js
deleted file mode 100644
index ef2ac16528..0000000000
--- a/frontend/src/components/svgIcons/view.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { PureComponent } from 'react';
-
-export class ViewIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/view.tsx b/frontend/src/components/svgIcons/view.tsx
new file mode 100644
index 0000000000..d36210cd0d
--- /dev/null
+++ b/frontend/src/components/svgIcons/view.tsx
@@ -0,0 +1,14 @@
+import { HTMLProps } from "react";
+
+export const ViewIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/waste.js b/frontend/src/components/svgIcons/waste.js
deleted file mode 100644
index 62e075193a..0000000000
--- a/frontend/src/components/svgIcons/waste.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { PureComponent } from 'react';
-
-export class WasteIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/waste.tsx b/frontend/src/components/svgIcons/waste.tsx
new file mode 100644
index 0000000000..c6ab05e19c
--- /dev/null
+++ b/frontend/src/components/svgIcons/waste.tsx
@@ -0,0 +1,14 @@
+import { HTMLProps } from "react";
+
+export const WasteIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/waterSanitation.js b/frontend/src/components/svgIcons/waterSanitation.js
deleted file mode 100644
index 305ccfac5d..0000000000
--- a/frontend/src/components/svgIcons/waterSanitation.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import { PureComponent } from 'react';
-
-export class WaterSanitationIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/waterSanitation.tsx b/frontend/src/components/svgIcons/waterSanitation.tsx
new file mode 100644
index 0000000000..b636236f02
--- /dev/null
+++ b/frontend/src/components/svgIcons/waterSanitation.tsx
@@ -0,0 +1,24 @@
+import { HTMLProps } from 'react';
+
+export const WaterSanitationIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/waves.js b/frontend/src/components/svgIcons/waves.js
deleted file mode 100644
index b730b3acb2..0000000000
--- a/frontend/src/components/svgIcons/waves.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import { PureComponent } from 'react';
-
-export class WavesIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/waves.tsx b/frontend/src/components/svgIcons/waves.tsx
new file mode 100644
index 0000000000..4c2fa954ba
--- /dev/null
+++ b/frontend/src/components/svgIcons/waves.tsx
@@ -0,0 +1,19 @@
+import { HTMLProps } from "react";
+
+export const WavesIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/worldNodes.js b/frontend/src/components/svgIcons/worldNodes.js
deleted file mode 100644
index cd5261b0ea..0000000000
--- a/frontend/src/components/svgIcons/worldNodes.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import { PureComponent } from 'react';
-
-export class WorldNodesIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/worldNodes.tsx b/frontend/src/components/svgIcons/worldNodes.tsx
new file mode 100644
index 0000000000..13936c3694
--- /dev/null
+++ b/frontend/src/components/svgIcons/worldNodes.tsx
@@ -0,0 +1,20 @@
+import { HTMLProps } from 'react';
+
+export const WorldNodesIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/youtube.js b/frontend/src/components/svgIcons/youtube.js
deleted file mode 100644
index b33ddff541..0000000000
--- a/frontend/src/components/svgIcons/youtube.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { PureComponent } from 'react';
-
-export class YoutubeIcon extends PureComponent {
- render() {
- return (
-
-
-
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/youtube.tsx b/frontend/src/components/svgIcons/youtube.tsx
new file mode 100644
index 0000000000..501a3ac9f7
--- /dev/null
+++ b/frontend/src/components/svgIcons/youtube.tsx
@@ -0,0 +1,14 @@
+import { HTMLProps } from "react";
+
+export const YoutubeIcon = (props: HTMLProps) => (
+
+
+
+
+
+
+);
diff --git a/frontend/src/components/svgIcons/zoomPlus.js b/frontend/src/components/svgIcons/zoomPlus.js
deleted file mode 100644
index 1d268b28cb..0000000000
--- a/frontend/src/components/svgIcons/zoomPlus.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { PureComponent } from 'react';
-
-// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
-// License: CC-By 4.0
-export class ZoomPlusIcon extends PureComponent {
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/frontend/src/components/svgIcons/zoomPlus.tsx b/frontend/src/components/svgIcons/zoomPlus.tsx
new file mode 100644
index 0000000000..52aea57732
--- /dev/null
+++ b/frontend/src/components/svgIcons/zoomPlus.tsx
@@ -0,0 +1,12 @@
+import { HTMLProps } from 'react';
+
+// Icon produced by FontAwesome project: https://github.com/FortAwesome/Font-Awesome/
+// License: CC-By 4.0
+export const ZoomPlusIcon = (props: HTMLProps) => (
+
+
+
+);
diff --git a/frontend/src/components/taskSelection/action.js b/frontend/src/components/taskSelection/action.js
index abe8b0258e..61d6eb343c 100644
--- a/frontend/src/components/taskSelection/action.js
+++ b/frontend/src/components/taskSelection/action.js
@@ -10,7 +10,7 @@ import messages from './messages';
import { ProjectInstructions } from './instructions';
import { TasksMap } from './map';
import { HeaderLine } from '../projectDetail/header';
-import { Button } from '../button';
+import { Button } from '../button.jsx';
import Portal from '../portal';
import { SidebarIcon } from '../svgIcons';
import { openEditor, getTaskGpxUrl, formatImageryUrl, formatJosmUrl } from '../../utils/openEditor';
@@ -58,11 +58,11 @@ export function TaskMapAction({ project, tasks, activeTasks, getTasks, action, e
() =>
activeTasks
? activeTasks
- .map((task) => task.taskId)
- .sort((n1, n2) => {
- // in ascending order
- return n1 - n2;
- })
+ .map((task) => task.taskId)
+ .sort((n1, n2) => {
+ // in ascending order
+ return n1 - n2;
+ })
: [],
[activeTasks],
);
diff --git a/frontend/src/components/taskSelection/extendSession.js b/frontend/src/components/taskSelection/extendSession.js
index d6278a8f74..d0fde5c3d1 100644
--- a/frontend/src/components/taskSelection/extendSession.js
+++ b/frontend/src/components/taskSelection/extendSession.js
@@ -2,7 +2,7 @@ import { useState } from 'react';
import { FormattedMessage } from 'react-intl';
import Popup from 'reactjs-popup';
-import { Button } from '../button';
+import { Button } from '../button.jsx';
import { Alert } from '../alert';
import { pushToLocalJSONAPI } from '../../network/genericJSONRequest';
import messages from './messages';
diff --git a/frontend/src/components/taskSelection/footer.js b/frontend/src/components/taskSelection/footer.js
index 03e6113dfe..214a2d2d3a 100644
--- a/frontend/src/components/taskSelection/footer.js
+++ b/frontend/src/components/taskSelection/footer.js
@@ -10,7 +10,7 @@ import { openEditor, formatJosmUrl } from '../../utils/openEditor';
import { useFetchLockedTasks } from '../../hooks/UseLockedTasks';
import { pushToLocalJSONAPI, fetchLocalJSONAPI } from '../../network/genericJSONRequest';
import { Dropdown } from '../dropdown';
-import { Button } from '../button';
+import { Button } from '../button.jsx';
import { Imagery } from './imagery';
import { MappingTypes } from '../mappingTypes';
import { LockedTaskModalContent } from './lockedTasks';
diff --git a/frontend/src/components/taskSelection/index.js b/frontend/src/components/taskSelection/index.tsx
similarity index 97%
rename from frontend/src/components/taskSelection/index.js
rename to frontend/src/components/taskSelection/index.tsx
index 6803d5f24b..7789489f54 100644
--- a/frontend/src/components/taskSelection/index.js
+++ b/frontend/src/components/taskSelection/index.tsx
@@ -31,6 +31,7 @@ import {
useTasksQuery,
} from '../../api/projects';
import { useTeamsQuery } from '../../api/teams';
+import { RootStore } from '../../store/index.js';
const TaskSelectionFooter = lazy(() => import('./footer'));
const getRandomTaskByAction = (activities, taskAction) => {
@@ -50,13 +51,16 @@ const getRandomTaskByAction = (activities, taskAction) => {
}
};
-export function TaskSelection({ project }: Object) {
+export function TaskSelection({ project }: {
+ // TODO: Fix this - big type
+ project: any
+}) {
useSetProjectPageTitleTag(project);
const { projectId } = project;
const location = useLocation();
const dispatch = useDispatch();
- const user = useSelector((state) => state.auth.userDetails);
- const userOrgs = useSelector((state) => state.auth.organisations);
+ const user = useSelector((state: RootStore) => state.auth.userDetails);
+ const userOrgs = useSelector((state: RootStore) => state.auth.organisations);
const lockedTasks = useGetLockedTasks();
const [zoomedTaskId, setZoomedTaskId] = useState(null);
const [activeSection, setActiveSection] = useState(null);
@@ -73,17 +77,17 @@ export function TaskSelection({ project }: Object) {
member: user.id,
},
{
- useErrorBoundary: true,
+ throwOnError: true,
},
);
const { data: activities, refetch: getActivities } = useActivitiesQuery(projectId);
const { data: contributions } = useProjectContributionsQuery(projectId, {
- useErrorBoundary: true,
+ throwOnError: true,
refetchOnWindowFocus: true,
refetchInterval: activeSection === 'contributions' ? 1000 * 60 : false,
});
const { data: tasksData, refetch: refetchTasks } = useTasksQuery(projectId, {
- useErrorBoundary: true,
+ throwOnError: true,
// Task status on the map were not being updated when coming from the action page,
// so added this as a workaround.
cacheTime: 0,
@@ -239,8 +243,8 @@ export function TaskSelection({ project }: Object) {
project.enforceRandomTaskSelection && taskAction !== 'validateSelectedTask'
? randomTask
: selected.length && !taskAction.endsWith('AnotherTask')
- ? selected
- : randomTask;
+ ? selected
+ : randomTask;
return (
diff --git a/frontend/src/components/taskSelection/lockedTasks.js b/frontend/src/components/taskSelection/lockedTasks.js
index 065d24e485..0c65467353 100644
--- a/frontend/src/components/taskSelection/lockedTasks.js
+++ b/frontend/src/components/taskSelection/lockedTasks.js
@@ -5,7 +5,7 @@ import { useSelector } from 'react-redux';
import { FormattedMessage } from 'react-intl';
import messages from './messages';
-import { Button } from '../button';
+import { Button } from '../button.jsx';
import { useGetLockedTasks } from '../../hooks/UseLockedTasks';
export function AnotherProjectLock({ projectId, lockedTasksLength, action }: Object) {
@@ -19,9 +19,9 @@ export function AnotherProjectLock({ projectId, lockedTasksLength, action }: Obj
1
- ? 'anotherProjectLockTextPlural'
- : 'anotherProjectLockTextSingular'
+ lockedTasksLength > 1
+ ? 'anotherProjectLockTextPlural'
+ : 'anotherProjectLockTextSingular'
]}
values={{
project: {projectId} ,
@@ -53,9 +53,9 @@ export function SameProjectLock({ lockedTasks, action }: Object) {
1
- ? 'currentProjectLockTextPlural'
- : 'currentProjectLockTextSingular'
+ lockedTasks.tasks.length > 1
+ ? 'currentProjectLockTextPlural'
+ : 'currentProjectLockTextSingular'
]}
values={{ taskId: {lockedTasks.tasks} }}
/>
diff --git a/frontend/src/components/taskSelection/messages.js b/frontend/src/components/taskSelection/messages.ts
similarity index 100%
rename from frontend/src/components/taskSelection/messages.js
rename to frontend/src/components/taskSelection/messages.ts
diff --git a/frontend/src/components/taskSelection/permissionErrorModal.js b/frontend/src/components/taskSelection/permissionErrorModal.js
index f9f34451c2..b94a193b22 100644
--- a/frontend/src/components/taskSelection/permissionErrorModal.js
+++ b/frontend/src/components/taskSelection/permissionErrorModal.js
@@ -4,7 +4,7 @@ import { FormattedMessage } from 'react-intl';
import messages from './messages';
import { getPermissionErrorMessage } from '../../utils/projectPermissions';
-import { Button } from '../button';
+import { Button } from '../button.jsx';
import { CloseIcon } from '../svgIcons';
import { TeamBox } from '../teamsAndOrgs/teams';
diff --git a/frontend/src/components/taskSelection/tabSelector.js b/frontend/src/components/taskSelection/tabSelector.tsx
similarity index 79%
rename from frontend/src/components/taskSelection/tabSelector.js
rename to frontend/src/components/taskSelection/tabSelector.tsx
index fbc67be72f..7b3001201b 100644
--- a/frontend/src/components/taskSelection/tabSelector.js
+++ b/frontend/src/components/taskSelection/tabSelector.tsx
@@ -2,7 +2,10 @@ import { FormattedMessage } from 'react-intl';
import messages from './messages';
-export const TabSelector = ({ activeSection, setActiveSection }) => (
+export const TabSelector = ({ activeSection, setActiveSection }: {
+ activeSection: string;
+ setActiveSection: (section: string) => void;
+}) => (
{['tasks', 'instructions', 'contributions'].map((section) => (
{
diff --git a/frontend/src/components/teamsAndOrgs/management.js b/frontend/src/components/teamsAndOrgs/management.jsx
similarity index 89%
rename from frontend/src/components/teamsAndOrgs/management.js
rename to frontend/src/components/teamsAndOrgs/management.jsx
index ec7b9561c5..d0589f6abb 100644
--- a/frontend/src/components/teamsAndOrgs/management.js
+++ b/frontend/src/components/teamsAndOrgs/management.jsx
@@ -6,7 +6,7 @@ import messages from './messages';
import { CustomButton } from '../button';
import { PlusIcon, WasteIcon } from '../svgIcons';
-export const ViewAllLink = ({ link }: Object) => (
+export const ViewAllLink = ({ link }) => (
@@ -23,7 +23,7 @@ export const AddButton = () => (
);
-export const DeleteButton = ({ className, onClick, showText = true }: Object) => {
+export const DeleteButton = ({ className, onClick, showText = true }) => {
const intl = useIntl();
return (
@@ -43,7 +43,7 @@ export const DeleteButton = ({ className, onClick, showText = true }: Object) =>
);
};
-export function VisibilityBox({ visibility, extraClasses }: Object) {
+export function VisibilityBox({ visibility, extraClasses }) {
let color = visibility === 'PUBLIC' ? 'blue-grey' : 'red';
let borderColor = visibility === 'PUBLIC' ? 'b--grey' : 'b--red';
const text = visibility ? : '';
@@ -78,9 +78,8 @@ export function Management(props) {
{props.isAdmin && (
{
props.setUserOnly(false);
}}
@@ -88,9 +87,8 @@ export function Management(props) {
{
props.setUserOnly(true);
}}
diff --git a/frontend/src/components/teamsAndOrgs/members.js b/frontend/src/components/teamsAndOrgs/members.js
index 8b84e783a4..ea210cd4af 100644
--- a/frontend/src/components/teamsAndOrgs/members.js
+++ b/frontend/src/components/teamsAndOrgs/members.js
@@ -7,7 +7,7 @@ import AsyncSelect from 'react-select/async';
import messages from './messages';
import { UserAvatar } from '../user/avatar';
import { EditModeControl } from './editMode';
-import { Button } from '../button';
+import { Button } from '../button.jsx';
import { SwitchToggle } from '../formInputs';
import { fetchLocalJSONAPI, pushToLocalJSONAPI } from '../../network/genericJSONRequest';
import { Alert } from '../alert';
diff --git a/frontend/src/components/teamsAndOrgs/messageMembers.js b/frontend/src/components/teamsAndOrgs/messageMembers.js
index c6c6698c0a..2656ff8154 100644
--- a/frontend/src/components/teamsAndOrgs/messageMembers.js
+++ b/frontend/src/components/teamsAndOrgs/messageMembers.js
@@ -4,7 +4,7 @@ import { FormattedMessage } from 'react-intl';
import toast from 'react-hot-toast';
import messages from './messages';
-import { Button } from '../button';
+import { Button } from '../button.jsx';
import { MessageStatus } from '../comments/status';
import { pushToLocalJSONAPI } from '../../network/genericJSONRequest';
import ReactPlaceholder from 'react-placeholder';
diff --git a/frontend/src/components/teamsAndOrgs/organisations.js b/frontend/src/components/teamsAndOrgs/organisations.js
index 40fca489f3..b5f93b887f 100644
--- a/frontend/src/components/teamsAndOrgs/organisations.js
+++ b/frontend/src/components/teamsAndOrgs/organisations.js
@@ -13,7 +13,7 @@ import { useUploadImage } from '../../hooks/UseUploadImage';
import { levels } from '../../hooks/UseOrganisationLevel';
import { Management } from './management';
import { InternalLinkIcon, ClipboardIcon } from '../svgIcons';
-import { Button } from '../button';
+import { Button } from '../button.jsx';
import { UserAvatarList } from '../user/avatar';
import { nCardPlaceholders } from './organisationsPlaceholder';
import { Alert } from '../alert';
diff --git a/frontend/src/components/user/avatar.js b/frontend/src/components/user/avatar.tsx
similarity index 75%
rename from frontend/src/components/user/avatar.js
rename to frontend/src/components/user/avatar.tsx
index 4ce38236d4..38231c74c4 100644
--- a/frontend/src/components/user/avatar.js
+++ b/frontend/src/components/user/avatar.tsx
@@ -5,15 +5,17 @@ import { ProfilePictureIcon, CloseIcon } from '../svgIcons';
import { getRandomArrayItem } from '../../utils/random';
import { useAvatarStyle } from '../../hooks/UseAvatarStyle';
import { useAvatarText } from '../../hooks/UseAvatarText';
+import { HtmlHTMLAttributes } from 'react';
+import { RootStore } from '../../store';
-export const CurrentUserAvatar = (props) => {
- const userPicture = useSelector((state) => state.auth.userDetails.pictureUrl);
+export const CurrentUserAvatar = (props: HtmlHTMLAttributes) => {
+ const userPicture = useSelector((state: RootStore) => state.auth.userDetails?.pictureUrl);
if (userPicture) {
return (
);
}
@@ -21,7 +23,6 @@ export const CurrentUserAvatar = (props) => {
};
export const UserAvatar = ({
- name,
username,
number,
picture,
@@ -30,7 +31,16 @@ export const UserAvatar = ({
removeFn,
editMode,
disableLink = false,
-}: Object) => {
+}: {
+ username: string;
+ number?: string;
+ picture?: string;
+ size?: string;
+ colorClasses?: string;
+ removeFn?: Function;
+ editMode?: boolean;
+ disableLink?: boolean;
+}) => {
const avatarText = useAvatarText(name, username, number);
if ((removeFn && editMode) || disableLink) {
@@ -62,7 +72,17 @@ export const UserAvatar = ({
}
};
-const Avatar = ({ username, size, colorClasses, removeFn, picture, text, editMode }) => {
+const Avatar = ({ username, size, colorClasses, removeFn, picture, text, editMode }: {
+ username: string;
+ number?: string;
+ picture?: string;
+ text: string;
+ size?: "small" | "medium" | "large";
+ colorClasses?: string;
+ removeFn?: Function;
+ editMode?: boolean;
+ disableLink?: boolean;
+}) => {
const { sizeClasses, textPadding, closeIconStyle, sizeStyle } = useAvatarStyle(
size,
editMode,
@@ -101,7 +121,14 @@ export const UserAvatarList = ({
bgColor,
size,
totalCount,
-}: Object) => {
+}: {
+ users: any[];
+ maxLength?: number;
+ textColor?: string;
+ bgColor?: string;
+ size?: string;
+ totalCount?: number;
+}) => {
const getColor = () =>
bgColor ? bgColor : getRandomArrayItem(['bg-orange', 'bg-red', 'bg-blue-dark', 'bg-blue-grey']);
let marginLeft = '-1.25rem';
@@ -127,11 +154,12 @@ export const UserAvatarList = ({
{maxLength && (totalCount || users.length) - maxLength > 0 && (
999
- ? 999
- : (totalCount || users.length) - maxLength
- }`}
+ // TODO: Fix this, it shouldn't really just be an empty string
+ username={""}
+ number={`+${(totalCount || users.length) - maxLength > 999
+ ? 999
+ : (totalCount || users.length) - maxLength
+ }`}
size={size}
colorClasses={`blue-dark bg-grey-light`}
disableLink={true}
diff --git a/frontend/src/components/user/forms/personalInformation.js b/frontend/src/components/user/forms/personalInformation.tsx
similarity index 95%
rename from frontend/src/components/user/forms/personalInformation.js
rename to frontend/src/components/user/forms/personalInformation.tsx
index bd6a20a202..69b084bf50 100644
--- a/frontend/src/components/user/forms/personalInformation.js
+++ b/frontend/src/components/user/forms/personalInformation.tsx
@@ -11,6 +11,7 @@ import { UserCountrySelect, RadioField } from '../../formInputs';
import { pushUserDetails } from '../../../store/actions/auth';
import { fetchLocalJSONAPI } from '../../../network/genericJSONRequest';
import { ORG_CODE } from '../../../config';
+import { RootStore } from '../../../store';
export const PROFILE_RELEVANT_FIELDS = [
'name',
@@ -66,7 +67,7 @@ const genderOptions = [
},
];
-const mapStateToProps = (state) => ({
+const mapStateToProps = (state: RootStore) => ({
userDetails: state.auth.userDetails,
token: state.auth.token,
});
@@ -95,14 +96,14 @@ function _PersonalInformationForm({ userDetails, token, pushUserDetails }) {
const composeValidators =
(...validators) =>
- (value) =>
- validators.reduce((error, validator) => error || validator(value), undefined);
+ (value) =>
+ validators.reduce((error, validator) => error || validator(value), undefined);
const isUrl = (value) =>
value &&
- value.match(
- /(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})/gi,
- ) ? (
+ value.match(
+ /(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})/gi,
+ ) ? (
) : undefined;
diff --git a/frontend/src/config/index.js b/frontend/src/config/index.ts
similarity index 55%
rename from frontend/src/config/index.js
rename to frontend/src/config/index.ts
index b5b11362c9..cdbf71fd2c 100644
--- a/frontend/src/config/index.js
+++ b/frontend/src/config/index.ts
@@ -1,66 +1,66 @@
// API ENDPOINTS
-export const API_VERSION = process.env.REACT_APP_API_VERSION || 'v2';
-export const API_URL = process.env.REACT_APP_API_URL
- ? new URL('/api/' + API_VERSION + '/', process.env.REACT_APP_API_URL)
+export const API_VERSION = import.meta.env.REACT_APP_API_VERSION || 'v2';
+export const API_URL = import.meta.env.REACT_APP_API_URL
+ ? new URL('/api/' + API_VERSION + '/', import.meta.env.REACT_APP_API_URL)
: 'http://127.0.0.1:5000/api/' + API_VERSION + '/';
export const OHSOME_STATS_BASE_URL =
- process.env.REACT_APP_OHSOME_STATS_BASE_URL || 'https://stats.now.ohsome.org/api';
+ import.meta.env.REACT_APP_OHSOME_STATS_BASE_URL || 'https://stats.now.ohsome.org/api';
// APPLICATION SETTINGS
-export const DEFAULT_LOCALE = process.env.REACT_APP_DEFAULT_LOCALE || 'en';
-export const ENVIRONMENT = process.env.REACT_APP_ENVIRONMENT || '';
+export const DEFAULT_LOCALE = import.meta.env.REACT_APP_DEFAULT_LOCALE || 'en';
+export const ENVIRONMENT = import.meta.env.REACT_APP_ENVIRONMENT || '';
export const PROJECTCARD_CONTRIBUTION_SHOWN_THRESHOLD =
- process.env.REACT_APP_PROJECTCARD_CONTRIBUTION_SHOWN_THRESHOLD || 5;
+ import.meta.env.REACT_APP_PROJECTCARD_CONTRIBUTION_SHOWN_THRESHOLD || 5;
export const INTERMEDIATE_LEVEL_COUNT =
- Number(process.env.REACT_APP_TM_MAPPER_LEVEL_INTERMEDIATE) || 250;
-export const ADVANCED_LEVEL_COUNT = Number(process.env.REACT_APP_TM_MAPPER_LEVEL_ADVANCED) || 500;
-export const MAPBOX_TOKEN = process.env.REACT_APP_MAPBOX_TOKEN || '';
-export const ENABLE_SERVICEWORKER = process.env.REACT_APP_ENABLE_SERVICEWORKER || 0;
-export const MAX_AOI_AREA = Number(process.env.REACT_APP_MAX_AOI_AREA) || 5000;
-export const MAX_FILESIZE = parseInt(process.env.REACT_APP_MAX_FILESIZE) || 1000000; // bytes
+ Number(import.meta.env.REACT_APP_TM_MAPPER_LEVEL_INTERMEDIATE) || 250;
+export const ADVANCED_LEVEL_COUNT = Number(import.meta.env.REACT_APP_TM_MAPPER_LEVEL_ADVANCED) || 500;
+export const MAPBOX_TOKEN = import.meta.env.REACT_APP_MAPBOX_TOKEN || '';
+export const ENABLE_SERVICEWORKER = import.meta.env.REACT_APP_ENABLE_SERVICEWORKER || 0;
+export const MAX_AOI_AREA = Number(import.meta.env.REACT_APP_MAX_AOI_AREA) || 5000;
+export const MAX_FILESIZE = parseInt(import.meta.env.REACT_APP_MAX_FILESIZE) || 1000000; // bytes
// ORGANISATIONAL INFORMATION
-export const ORG_NAME = process.env.REACT_APP_ORG_NAME || 'Humanitarian OpenStreetMap Team';
-export const ORG_CODE = process.env.REACT_APP_ORG_CODE || 'HOT';
-export const ORG_URL = process.env.REACT_APP_ORG_URL || '';
-export const ORG_LOGO = process.env.REACT_APP_ORG_LOGO || '';
-export const HOMEPAGE_IMG_HIGH = process.env.REACT_APP_HOMEPAGE_IMG_HIGH || '';
-export const HOMEPAGE_IMG_LOW = process.env.REACT_APP_HOMEPAGE_IMG_LOW || '';
-export const OSM_CLIENT_ID = process.env.REACT_APP_OSM_CLIENT_ID || '';
-export const OSM_CLIENT_SECRET = process.env.REACT_APP_OSM_CLIENT_SECRET || '';
-export const OSM_REDIRECT_URI = process.env.REACT_APP_OSM_REDIRECT_URI || '';
-export const ORG_PRIVACY_POLICY_URL = process.env.REACT_APP_ORG_PRIVACY_POLICY_URL || '';
+export const ORG_NAME = import.meta.env.REACT_APP_ORG_NAME || 'Humanitarian OpenStreetMap Team';
+export const ORG_CODE = import.meta.env.REACT_APP_ORG_CODE || 'HOT';
+export const ORG_URL = import.meta.env.REACT_APP_ORG_URL || '';
+export const ORG_LOGO = import.meta.env.REACT_APP_ORG_LOGO || '';
+export const HOMEPAGE_IMG_HIGH = import.meta.env.REACT_APP_HOMEPAGE_IMG_HIGH || '';
+export const HOMEPAGE_IMG_LOW = import.meta.env.REACT_APP_HOMEPAGE_IMG_LOW || '';
+export const OSM_CLIENT_ID = import.meta.env.REACT_APP_OSM_CLIENT_ID || '';
+export const OSM_CLIENT_SECRET = import.meta.env.REACT_APP_OSM_CLIENT_SECRET || '';
+export const OSM_REDIRECT_URI = import.meta.env.REACT_APP_OSM_REDIRECT_URI || '';
+export const ORG_PRIVACY_POLICY_URL = import.meta.env.REACT_APP_ORG_PRIVACY_POLICY_URL || '';
export const OSM_REGISTER_URL =
- process.env.REACT_APP_OSM_REGISTER_URL || 'https://www.openstreetmap.org/user/new';
-export const ORG_TWITTER = process.env.REACT_APP_ORG_TWITTER || 'https://twitter.com/hotosm';
-export const ORG_FB = process.env.REACT_APP_ORG_FB || 'https://www.facebook.com/hotosm';
+ import.meta.env.REACT_APP_OSM_REGISTER_URL || 'https://www.openstreetmap.org/user/new';
+export const ORG_TWITTER = import.meta.env.REACT_APP_ORG_TWITTER || 'https://twitter.com/hotosm';
+export const ORG_FB = import.meta.env.REACT_APP_ORG_FB || 'https://www.facebook.com/hotosm';
export const ORG_INSTAGRAM =
- process.env.REACT_APP_ORG_INSTAGRAM || 'https://www.instagram.com/open.mapping.hubs/';
+ import.meta.env.REACT_APP_ORG_INSTAGRAM || 'https://www.instagram.com/open.mapping.hubs/';
export const ORG_YOUTUBE =
- process.env.REACT_APP_ORG_YOUTUBE || 'https://www.youtube.com/user/hotosm';
-export const ORG_GITHUB = process.env.REACT_APP_ORG_GITHUB || 'https://github.com/hotosm';
-export const MATOMO_ID = process.env.REACT_APP_MATOMO_ID || '';
-export const SERVICE_DESK = process.env.REACT_APP_SERVICE_DESK || '';
-export const IMAGE_UPLOAD_SERVICE = process.env.REACT_APP_IMAGE_UPLOAD_API_URL || '';
+ import.meta.env.REACT_APP_ORG_YOUTUBE || 'https://www.youtube.com/user/hotosm';
+export const ORG_GITHUB = import.meta.env.REACT_APP_ORG_GITHUB || 'https://github.com/hotosm';
+export const MATOMO_ID = import.meta.env.REACT_APP_MATOMO_ID || '';
+export const SERVICE_DESK = import.meta.env.REACT_APP_SERVICE_DESK || '';
+export const IMAGE_UPLOAD_SERVICE = import.meta.env.REACT_APP_IMAGE_UPLOAD_API_URL || '';
export const TM_DEFAULT_CHANGESET_COMMENT =
- process.env.REACT_APP_TM_DEFAULT_CHANGESET_COMMENT || '#hotosm-project';
-export const HOMEPAGE_VIDEO_URL = process.env.REACT_APP_HOMEPAGE_VIDEO_URL || '';
+ import.meta.env.REACT_APP_TM_DEFAULT_CHANGESET_COMMENT || '#hotosm-project';
+export const HOMEPAGE_VIDEO_URL = import.meta.env.REACT_APP_HOMEPAGE_VIDEO_URL || '';
// Sentry.io DSN
-export const SENTRY_FRONTEND_DSN = process.env.REACT_APP_SENTRY_FRONTEND_DSN;
+export const SENTRY_FRONTEND_DSN = import.meta.env.REACT_APP_SENTRY_FRONTEND_DSN;
// OSM API and Editor URLs
export const OSM_SERVER_URL =
- process.env.REACT_APP_OSM_SERVER_URL || 'https://www.openstreetmap.org';
+ import.meta.env.REACT_APP_OSM_SERVER_URL || 'https://www.openstreetmap.org';
export const OSM_SERVER_API_URL =
- process.env.REACT_APP_OSM_SERVER_API_URL || 'https://api.openstreetmap.org';
+ import.meta.env.REACT_APP_OSM_SERVER_API_URL || 'https://api.openstreetmap.org';
export const ID_EDITOR_URL =
- process.env.REACT_APP_ID_EDITOR_URL || 'https://www.openstreetmap.org/edit?editor=id&';
+ import.meta.env.REACT_APP_ID_EDITOR_URL || 'https://www.openstreetmap.org/edit?editor=id&';
export const POTLATCH2_EDITOR_URL =
- process.env.REACT_APP_POTLATCH2_EDITOR_URL ||
+ import.meta.env.REACT_APP_POTLATCH2_EDITOR_URL ||
'https://www.openstreetmap.org/edit?editor=potlatch2';
export const RAPID_EDITOR_URL =
- process.env.REACT_APP_RAPID_EDITOR_URL || 'https://mapwith.ai/rapid';
-export const EXPORT_TOOL_S3_URL = process.env.REACT_APP_EXPORT_TOOL_S3_URL || '';
-export const ENABLE_EXPORT_TOOL = process.env.REACT_APP_ENABLE_EXPORT_TOOL || '';
+ import.meta.env.REACT_APP_RAPID_EDITOR_URL || 'https://mapwith.ai/rapid';
+export const EXPORT_TOOL_S3_URL = import.meta.env.REACT_APP_EXPORT_TOOL_S3_URL || '';
+export const ENABLE_EXPORT_TOOL = import.meta.env.REACT_APP_ENABLE_EXPORT_TOOL || '';
export const TASK_COLOURS = {
READY: '#fff',
@@ -173,7 +173,7 @@ export const MAP_STYLE = MAPBOX_TOKEN
export const MAPBOX_RTL_PLUGIN_URL =
'https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-rtl-text/v0.2.0/mapbox-gl-rtl-text.js';
-export const UNDERPASS_URL = process.env.REACT_APP_UNDERPASS_URL || 'https://underpass.hotosm.org';
+export const UNDERPASS_URL = import.meta.env.REACT_APP_UNDERPASS_URL || 'https://underpass.hotosm.org';
export const DROPZONE_SETTINGS = {
accept: {
diff --git a/frontend/src/globalFix.ts b/frontend/src/globalFix.ts
new file mode 100644
index 0000000000..d95f66e90d
--- /dev/null
+++ b/frontend/src/globalFix.ts
@@ -0,0 +1,2 @@
+// https://stackoverflow.com/questions/72114775/vite-global-is-not-defined
+window.global ||= window;
diff --git a/frontend/src/hooks/UseAsync.js b/frontend/src/hooks/UseAsync.ts
similarity index 90%
rename from frontend/src/hooks/UseAsync.js
rename to frontend/src/hooks/UseAsync.ts
index 1f41c7a16e..25010a6620 100644
--- a/frontend/src/hooks/UseAsync.js
+++ b/frontend/src/hooks/UseAsync.ts
@@ -2,7 +2,7 @@ import { useState, useCallback, useEffect } from 'react';
// source: https://usehooks.com/useAsync/ (with modifications)
-export const useAsync = (asyncFunction, immediate = false) => {
+export const useAsync = (asyncFunction: () => Promise, immediate = false) => {
const [status, setStatus] = useState('idle');
const [value, setValue] = useState(null);
const [error, setError] = useState(null);
@@ -12,7 +12,7 @@ export const useAsync = (asyncFunction, immediate = false) => {
// useCallback ensures the below useEffect is not called
// on every render, but only if asyncFunction changes.
const execute = useCallback(
- (param = null) => {
+ async (param = null) => {
setStatus('pending');
setValue(null);
setError(null);
diff --git a/frontend/src/hooks/UseAvatarStyle.js b/frontend/src/hooks/UseAvatarStyle.tsx
similarity index 81%
rename from frontend/src/hooks/UseAvatarStyle.js
rename to frontend/src/hooks/UseAvatarStyle.tsx
index e309f41738..7c28397253 100644
--- a/frontend/src/hooks/UseAvatarStyle.js
+++ b/frontend/src/hooks/UseAvatarStyle.tsx
@@ -1,10 +1,10 @@
import { useState, useEffect } from 'react';
-export const useAvatarStyle = (size, editMode, picture) => {
+export const useAvatarStyle = (size?: "small" | "medium" | "large", editMode?: boolean, picture?: string) => {
const [sizeClasses, setSizeClasses] = useState('h2 w2 f5');
const [textPadding, setTextPadding] = useState({});
const [sizeStyle, setSizeStyle] = useState({});
- const [closeIconStyle, setCloseIconStyle] = useState({ left: '0.4rem' });
+ const [closeIconStyle, setCloseIconStyle] = useState>({ left: '0.4rem' });
useEffect(() => {
if (size === 'large') setSizeClasses('h3 w3 f2');
@@ -26,7 +26,7 @@ export const useAvatarStyle = (size, editMode, picture) => {
}, [size]);
useEffect(() => {
if (size === 'small') {
- const smallStyle = { height: '1.5rem', width: '1.5rem' };
+ const smallStyle: Partial = { height: '1.5rem', width: '1.5rem' };
if (picture) smallStyle.backgroundImage = `url("${picture}")`;
setSizeStyle(smallStyle);
} else {
diff --git a/frontend/src/hooks/UseFetch.js b/frontend/src/hooks/UseFetch.ts
similarity index 63%
rename from frontend/src/hooks/UseFetch.js
rename to frontend/src/hooks/UseFetch.ts
index 97fc5484ef..3b43ee0367 100644
--- a/frontend/src/hooks/UseFetch.js
+++ b/frontend/src/hooks/UseFetch.ts
@@ -3,13 +3,14 @@ import { useSelector } from 'react-redux';
import { fetchLocalJSONAPI, fetchLocalJSONAPIWithAbort } from '../network/genericJSONRequest';
import { useInterval } from './UseInterval';
+import { RootStore } from '../store';
-export const useFetch = (url, trigger = true) => {
- const token = useSelector((state) => state.auth.token);
- const locale = useSelector((state) => state.preferences['locale']);
- const [error, setError] = useState(null);
+export const useFetch = (url: string, trigger: boolean = true) => {
+ const token = useSelector((state: RootStore) => state.auth.token);
+ const locale = useSelector((state: RootStore) => state.preferences['locale']);
+ const [error, setError] = useState(null);
const [loading, setLoading] = useState(true);
- const [data, setData] = useState({});
+ const [data, setData] = useState({});
useEffect(() => {
(async () => {
@@ -26,8 +27,10 @@ export const useFetch = (url, trigger = true) => {
setData(response);
setLoading(false);
} catch (e) {
- setError(e);
- setLoading(false);
+ if (e instanceof Error) {
+ setError(e.message);
+ setLoading(false);
+ }
}
}
})();
@@ -35,12 +38,12 @@ export const useFetch = (url, trigger = true) => {
return [error, loading, data];
};
-export const useFetchWithAbort = (url, trigger = true) => {
- const token = useSelector((state) => state.auth.token);
- const locale = useSelector((state) => state.preferences['locale']);
- const [error, setError] = useState(null);
+export const useFetchWithAbort = (url: string, trigger: boolean = true) => {
+ const token = useSelector((state: RootStore) => state.auth.token);
+ const locale = useSelector((state: RootStore) => state.preferences['locale']);
+ const [error, setError] = useState(null);
const [loading, setLoading] = useState(true);
- const [data, setData] = useState({});
+ const [data, setData] = useState(null);
const [refetchIndex, setRefetchIndex] = useState(0);
// Component using refetch would infinitely make requests
@@ -68,8 +71,10 @@ export const useFetchWithAbort = (url, trigger = true) => {
setLoading(false);
} catch (e) {
if (signal.aborted) return;
- setError(e);
- setLoading(false);
+ if (e instanceof Error) {
+ setError(e.message);
+ setLoading(false);
+ }
}
}
})();
@@ -79,11 +84,11 @@ export const useFetchWithAbort = (url, trigger = true) => {
return [error, loading, data, refetch];
};
-export function useFetchIntervaled(url, delay, trigger = true) {
- const token = useSelector((state) => state.auth.token);
- const locale = useSelector((state) => state.preferences['locale']);
+export function useFetchIntervaled(url: string, delay: number, trigger = true) {
+ const token = useSelector((state: RootStore) => state.auth.token);
+ const locale = useSelector((state: RootStore) => state.preferences['locale']);
const [data, setData] = useState();
- const [error, setError] = useState(null);
+ const [error, setError] = useState(null);
useInterval(() => {
(async () => {
@@ -93,7 +98,12 @@ export function useFetchIntervaled(url, delay, trigger = true) {
const response = await fetchLocalJSONAPI(url, token, 'GET', locale.replace('-', '_'));
setData(response);
} catch (e) {
- setError(e);
+ if (e instanceof Error) {
+ if (e.name === 'AbortError') {
+ return;
+ }
+ setError(e.message);
+ }
}
}
})();
diff --git a/frontend/src/hooks/UseFirstTaskActionDate.js b/frontend/src/hooks/UseFirstTaskActionDate.ts
similarity index 75%
rename from frontend/src/hooks/UseFirstTaskActionDate.js
rename to frontend/src/hooks/UseFirstTaskActionDate.ts
index d57489f9a0..c41fa3eec6 100644
--- a/frontend/src/hooks/UseFirstTaskActionDate.js
+++ b/frontend/src/hooks/UseFirstTaskActionDate.ts
@@ -2,8 +2,10 @@ import { useEffect, useState } from 'react';
import { compareHistoryLastUpdate } from '../utils/sorting';
-const useFirstTaskActionDate = (history) => {
- const [firstDate, setFirstDate] = useState(null);
+const useFirstTaskActionDate = (history: {
+ taskHistory: { actionDate: string }[];
+}) => {
+ const [firstDate, setFirstDate] = useState(null);
useEffect(() => {
if (history && history.taskHistory && history.taskHistory.length) {
const fistTaskAction =
diff --git a/frontend/src/hooks/UseGetContributors.js b/frontend/src/hooks/UseGetContributors.ts
similarity index 69%
rename from frontend/src/hooks/UseGetContributors.js
rename to frontend/src/hooks/UseGetContributors.ts
index 961e6e209d..f40d967a44 100644
--- a/frontend/src/hooks/UseGetContributors.js
+++ b/frontend/src/hooks/UseGetContributors.ts
@@ -1,9 +1,11 @@
import { useCallback } from 'react';
-const useGetContributors = (history) => {
+const useGetContributors = (history: {
+ taskHistory: { usernames: string[]; actionBy: string }[];
+}) => {
const getContributors = useCallback(() => {
if (history && history.taskHistory && history.taskHistory.length) {
- return history.taskHistory.reduce((usernames, item) => {
+ return history.taskHistory.reduce((usernames, item) => {
if (!usernames.includes(item.actionBy)) {
usernames.push(item.actionBy);
}
diff --git a/frontend/src/hooks/UseInterval.js b/frontend/src/hooks/UseInterval.ts
similarity index 69%
rename from frontend/src/hooks/UseInterval.js
rename to frontend/src/hooks/UseInterval.ts
index 0a2d2163f9..40d569aba8 100644
--- a/frontend/src/hooks/UseInterval.js
+++ b/frontend/src/hooks/UseInterval.ts
@@ -1,7 +1,7 @@
import { useEffect, useRef } from 'react';
-export function useInterval(callback, delay) {
- const savedCallback = useRef();
+export function useInterval(callback: () => void, delay: number) {
+ const savedCallback = useRef void)>();
// Remember the latest callback.
useEffect(() => {
@@ -11,7 +11,7 @@ export function useInterval(callback, delay) {
// Set up the interval.
useEffect(() => {
function tick() {
- savedCallback.current();
+ savedCallback.current?.();
}
if (delay !== null) {
let id = setInterval(tick, delay);
diff --git a/frontend/src/hooks/UseThrottle.js b/frontend/src/hooks/UseThrottle.ts
similarity index 89%
rename from frontend/src/hooks/UseThrottle.js
rename to frontend/src/hooks/UseThrottle.ts
index 54518c36cc..c73282a0ec 100644
--- a/frontend/src/hooks/UseThrottle.js
+++ b/frontend/src/hooks/UseThrottle.ts
@@ -1,7 +1,7 @@
/* https://github.com/bhaskarGyan/use-throttle#readme */
import { useState, useEffect, useRef, useCallback } from 'react';
-export const useThrottle = (value, limit) => {
+export const useThrottle = (value: string, limit: number) => {
const [throttledValue, setThrottledValue] = useState(value);
const lastRan = useRef(Date.now());
@@ -22,19 +22,24 @@ export const useThrottle = (value, limit) => {
};
/* https://github.com/xnimorz/use-debounce based on 3.3.0 compiled from TypeScript */
-export function useDebouncedCallback(callback, delay, options = {}) {
+export function useDebouncedCallback(callback: (arg: T) => void, delay: number, options: {
+ maxWait?: number;
+ leading?: boolean;
+ trailing?: boolean;
+} = {}) {
const maxWait = options.maxWait;
- const maxWaitHandler = useRef(null);
+ const maxWaitHandler = useRef(null);
const maxWaitArgs = useRef([]);
const leading = options.leading;
const trailing = options.trailing === undefined ? true : options.trailing;
const leadingCall = useRef(false);
- const functionTimeoutHandler = useRef(null);
+ const functionTimeoutHandler = useRef(null);
const isComponentUnmounted = useRef(false);
const debouncedFunction = useRef(callback);
debouncedFunction.current = callback;
const cancelDebouncedCallback = useCallback(() => {
- clearTimeout(functionTimeoutHandler.current);
+ if (!functionTimeoutHandler.current || !maxWaitHandler.current) return;
+ clearTimeout(functionTimeoutHandler?.current);
clearTimeout(maxWaitHandler.current);
maxWaitHandler.current = null;
maxWaitArgs.current = [];
@@ -49,9 +54,11 @@ export function useDebouncedCallback(callback, delay, options = {}) {
[],
);
const debouncedCallback = useCallback(
- (...args) => {
+ (...args: any) => {
maxWaitArgs.current = args;
- clearTimeout(functionTimeoutHandler.current);
+ if (functionTimeoutHandler.current) {
+ clearTimeout(functionTimeoutHandler.current);
+ }
if (leadingCall.current) {
leadingCall.current = false;
}
diff --git a/frontend/src/hooks/UseTimeDiff.js b/frontend/src/hooks/UseTimeDiff.js
deleted file mode 100644
index ebde6f685b..0000000000
--- a/frontend/src/hooks/UseTimeDiff.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { useState, useEffect } from 'react';
-
-export function useTimeDiff(tasksByDay) {
- const [unit, setUnit] = useState('day');
- const day = 86400000;
- useEffect(() => {
- if (tasksByDay && tasksByDay.length >= 2) {
- const timeDiff =
- (new Date(tasksByDay[tasksByDay.length - 1].date) - new Date(tasksByDay[0].date)) / day;
- if (timeDiff > 16 * 7) {
- setUnit('month');
- } else if (timeDiff > 16) {
- setUnit('week');
- }
- }
- }, [tasksByDay]);
- return unit;
-}
diff --git a/frontend/src/hooks/UseTimeDiff.ts b/frontend/src/hooks/UseTimeDiff.ts
new file mode 100644
index 0000000000..969ef807dc
--- /dev/null
+++ b/frontend/src/hooks/UseTimeDiff.ts
@@ -0,0 +1,19 @@
+import type { TimeUnit } from 'chart.js';
+import { differenceInDays } from 'date-fns';
+import { useState, useEffect } from 'react';
+
+export function useTimeDiff(tasksByDay: { date: string }[]) {
+ const [unit, setUnit] = useState('day');
+ useEffect(() => {
+ if (tasksByDay && tasksByDay.length >= 2) {
+ const timeDiff = differenceInDays(tasksByDay[tasksByDay.length - 1].date, new Date(tasksByDay[0].date))
+ // If the time difference is greater than 16 weeks, set the unit to month
+ if (timeDiff > 16 * 7) {
+ setUnit('month');
+ } else if (timeDiff > 16) { // If the time difference is greater than 16 days, set the unit to week
+ setUnit('week');
+ }
+ }
+ }, [tasksByDay]);
+ return unit;
+}
diff --git a/frontend/src/hooks/UseUploadImage.js b/frontend/src/hooks/UseUploadImage.ts
similarity index 83%
rename from frontend/src/hooks/UseUploadImage.js
rename to frontend/src/hooks/UseUploadImage.ts
index 55ba8fc58e..82720711df 100644
--- a/frontend/src/hooks/UseUploadImage.js
+++ b/frontend/src/hooks/UseUploadImage.ts
@@ -3,12 +3,13 @@ import { useSelector } from 'react-redux';
import { pushToLocalJSONAPI } from '../network/genericJSONRequest';
import { slugifyFileName } from '../utils/slugifyFileName';
+import { RootStore } from '../store';
export const useUploadImage = () => {
const [uploading, setUploading] = useState(false);
const [uploadError, setUploadError] = useState(null);
- const uploadImg = useCallback((file, updateFn, token) => {
+ const uploadImg = useCallback((file: File, updateFn: any, token: string) => {
if (file && updateFn && token) {
const promise = new Promise((resolve, reject) => {
const reader = new FileReader();
@@ -25,6 +26,7 @@ export const useUploadImage = () => {
(result) => {
const payload = JSON.stringify({
mime: file.type,
+ // @ts-expect-error TS Migrations
data: result.split('base64,')[1],
filename: slugifyFileName(file.name, file.type),
});
@@ -49,12 +51,14 @@ export const useUploadImage = () => {
return [uploadError, uploading, uploadImg];
};
-export const useOnDrop = (appendImgToComment) => {
- const token = useSelector((state) => state.auth.token);
+export const useOnDrop = (appendImgToComment: string) => {
+ const token = useSelector((state: RootStore) => state.auth.token);
const [uploadError, uploading, uploadImg] = useUploadImage();
const onDrop = useCallback(
+ // @ts-expect-error TS Migrations
(acceptedFiles) => {
+ // @ts-expect-error TS Migrations
acceptedFiles.forEach(async (file) => await uploadImg(file, appendImgToComment, token));
},
[token, uploadImg, appendImgToComment],
diff --git a/frontend/src/index.js b/frontend/src/index.tsx
similarity index 94%
rename from frontend/src/index.js
rename to frontend/src/index.tsx
index 6f8255a64c..bd4f683436 100644
--- a/frontend/src/index.js
+++ b/frontend/src/index.tsx
@@ -1,3 +1,4 @@
+import "./globalFix"
import { createRoot } from 'react-dom/client';
import { PersistGate } from 'redux-persist/integration/react';
import { Provider } from 'react-redux';
@@ -5,9 +6,9 @@ import { load } from 'webfontloader';
import { init, BrowserTracing, Replay } from '@sentry/react';
import 'react-tooltip/dist/react-tooltip.css'; // Needed by for { Tooltip } from 'react-tooltip' to work properly
-import App from './App';
+import App from './App.jsx';
import { store, persistor } from './store';
-import { ConnectedIntl } from './utils/internationalization';
+import { ConnectedIntl } from './utils/internationalization.jsx';
import { register, unregister, onServiceWorkerUpdate } from './serviceWorkerRegistration';
import { ENABLE_SERVICEWORKER, SENTRY_FRONTEND_DSN, ENVIRONMENT } from './config';
diff --git a/frontend/src/network/genericJSONRequest.js b/frontend/src/network/genericJSONRequest.ts
similarity index 80%
rename from frontend/src/network/genericJSONRequest.js
rename to frontend/src/network/genericJSONRequest.ts
index 6bb9af7b08..34e0e50b7e 100644
--- a/frontend/src/network/genericJSONRequest.js
+++ b/frontend/src/network/genericJSONRequest.ts
@@ -7,11 +7,12 @@ import { API_URL } from '../config';
* @param {RequestInit} [init={}}] Any specific init options you want to pass the fetch (such as an {@link AbortSignal})
* @returns {Promise<*>} A promise that returns a JSON or an error
*/
-export function fetchExternalJSONAPI(url, init = {}): Promise<*> {
- if (!init.headers) {
- init.headers = { 'Content-Type': 'application/json' };
+export function fetchExternalJSONAPI(url: string, init?: {
+ headers: any
+}) {
+ if (init?.headers) {
+ init.headers['Content-Type'] = 'application/json';
}
- init.headers['Content-Type'] = 'application/json';
return fetch(url, {
method: 'GET',
@@ -23,12 +24,13 @@ export function fetchExternalJSONAPI(url, init = {}): Promise<*> {
});
}
-export function fetchLocalJSONAPI(endpoint, token, method = 'GET', language = 'en'): Promise<*> {
+export function fetchLocalJSONAPI(endpoint: string, token: string, method = 'GET', language = 'en') {
const url = new URL(endpoint, API_URL);
let headers = {
'Content-Type': 'application/json',
'Accept-Language': language.replace('-', '_'),
- };
+ } as Record;
+
if (token) {
headers['Authorization'] = `Token ${token}`;
}
@@ -43,9 +45,9 @@ export function fetchLocalJSONAPI(endpoint, token, method = 'GET', language = 'e
}
export function fetchLocalJSONAPIWithAbort(
- endpoint,
- token,
- signal,
+ endpoint: string,
+ token: string,
+ signal: AbortSignal,
method = 'GET',
language = 'en',
) {
@@ -53,7 +55,7 @@ export function fetchLocalJSONAPIWithAbort(
let headers = {
'Content-Type': 'application/json',
'Accept-Language': language.replace('-', '_'),
- };
+ } as Record
if (token) {
headers['Authorization'] = `Token ${token}`;
}
@@ -69,12 +71,12 @@ export function fetchLocalJSONAPIWithAbort(
}
export function pushToLocalJSONAPI(
- endpoint,
- payload,
- token,
+ endpoint: string,
+ payload: string,
+ token: string,
method = 'POST',
language = 'en',
-): Promise<*> {
+) {
const url = new URL(endpoint, API_URL);
return fetch(url, {
method: method,
diff --git a/frontend/src/network/tests/mockData/auth.js b/frontend/src/network/tests/mockData/auth.ts
similarity index 100%
rename from frontend/src/network/tests/mockData/auth.js
rename to frontend/src/network/tests/mockData/auth.ts
diff --git a/frontend/src/network/tests/mockData/contributions.js b/frontend/src/network/tests/mockData/contributions.ts
similarity index 100%
rename from frontend/src/network/tests/mockData/contributions.js
rename to frontend/src/network/tests/mockData/contributions.ts
diff --git a/frontend/src/network/tests/mockData/featuredProjects.js b/frontend/src/network/tests/mockData/featuredProjects.ts
similarity index 100%
rename from frontend/src/network/tests/mockData/featuredProjects.js
rename to frontend/src/network/tests/mockData/featuredProjects.ts
diff --git a/frontend/src/network/tests/mockData/homepageStats.js b/frontend/src/network/tests/mockData/homepageStats.ts
similarity index 100%
rename from frontend/src/network/tests/mockData/homepageStats.js
rename to frontend/src/network/tests/mockData/homepageStats.ts
diff --git a/frontend/src/network/tests/mockData/management.js b/frontend/src/network/tests/mockData/management.ts
similarity index 98%
rename from frontend/src/network/tests/mockData/management.js
rename to frontend/src/network/tests/mockData/management.ts
index 63d5cbfa61..3aad78fd68 100644
--- a/frontend/src/network/tests/mockData/management.js
+++ b/frontend/src/network/tests/mockData/management.ts
@@ -99,7 +99,7 @@ export const interestCreationSuccess = (name) => ({
name,
});
-export const interestUpdationSuccess = (name) => ({
+export const interestUpdationSuccess = (name: string) => ({
id: 123,
name,
});
diff --git a/frontend/src/network/tests/mockData/miscellaneous.js b/frontend/src/network/tests/mockData/miscellaneous.ts
similarity index 100%
rename from frontend/src/network/tests/mockData/miscellaneous.js
rename to frontend/src/network/tests/mockData/miscellaneous.ts
diff --git a/frontend/src/network/tests/mockData/notifications.js b/frontend/src/network/tests/mockData/notifications.ts
similarity index 91%
rename from frontend/src/network/tests/mockData/notifications.js
rename to frontend/src/network/tests/mockData/notifications.ts
index fc5ec7dd4f..b7e3a1b176 100644
--- a/frontend/src/network/tests/mockData/notifications.js
+++ b/frontend/src/network/tests/mockData/notifications.ts
@@ -1,4 +1,4 @@
-export function generateSampleNotifications(numSamples) {
+export function generateSampleNotifications(numSamples: number) {
const sampleData = [];
for (let i = 0; i < numSamples; i++) {
@@ -11,8 +11,8 @@ export function generateSampleNotifications(numSamples) {
i % 3 === 0
? 'REQUEST_TEAM_NOTIFICATION'
: i % 3 === 1
- ? 'INVITATION_NOTIFICATION'
- : 'TEAM_BROADCAST';
+ ? 'INVITATION_NOTIFICATION'
+ : 'TEAM_BROADCAST';
const sentDate = new Date().toISOString();
const read = i % 3 !== 0;
diff --git a/frontend/src/network/tests/mockData/projects.js b/frontend/src/network/tests/mockData/projects.ts
similarity index 97%
rename from frontend/src/network/tests/mockData/projects.js
rename to frontend/src/network/tests/mockData/projects.ts
index 258bf929e5..48ce6b0a8e 100644
--- a/frontend/src/network/tests/mockData/projects.js
+++ b/frontend/src/network/tests/mockData/projects.ts
@@ -13,9 +13,9 @@ const status = {
[PROJECT_ID_ALL_MAPPED]: {
mappedPercent: 100,
},
-};
+} as const;
-export const getProjectSummary = (id) => ({
+export const getProjectSummary = (id: number) => ({
projectId: id,
defaultLocale: 'en',
author: 'test_user',
@@ -40,8 +40,8 @@ export const getProjectSummary = (id) => ({
osmchaFilterId: '9322aa63-cccc-4d0d-9f93-403678e52345',
mappingTypes: ['BUILDINGS'],
changesetComment: `${TM_DEFAULT_CHANGESET_COMMENT}-${id} #brumado-buildings`,
- percentMapped: status[id]?.mappedPercent || 16,
- percentValidated: status[id]?.validatedPercent || 6,
+ percentMapped: (id === PROJECT_ID_ALL_VALIDATED || id === PROJECT_ID_ALL_MAPPED) ? status[id].mappedPercent : 16,
+ percentValidated: (id === PROJECT_ID_ALL_VALIDATED) ? status[id].validatedPercent : 6,
percentBadImagery: 0,
aoiCentroid: {
type: 'Point',
@@ -73,7 +73,7 @@ export const getProjectSummary = (id) => ({
validationEditors: ['JOSM', 'POTLATCH_2', 'FIELD_PAPERS', 'ID'],
});
-export const getProjectStats = (id) => ({
+export const getProjectStats = (id: string) => ({
projectId: Number(id),
'projectArea(in sq.km)': 3506.03997973834,
totalMappers: 105,
@@ -294,7 +294,7 @@ export const userTouchedProjects = {
],
};
-export const taskDetail = (taskId) => ({
+export const taskDetail = (taskId: number) => ({
taskId: taskId,
projectId: 5871,
taskStatus: 'LOCKED_FOR_MAPPING',
@@ -381,11 +381,11 @@ export const userFavorite = {
favorited: false,
};
-export const favoritePost = (id) => ({
+export const favoritePost = () => ({
project_id: 123,
});
-export const activities = (id) => {
+export const activities = (id: number) => {
const isProjectValidated = id === PROJECT_ID_ALL_VALIDATED;
return {
activity: [
diff --git a/frontend/src/network/tests/mockData/taskGrid.js b/frontend/src/network/tests/mockData/taskGrid.ts
similarity index 100%
rename from frontend/src/network/tests/mockData/taskGrid.js
rename to frontend/src/network/tests/mockData/taskGrid.ts
diff --git a/frontend/src/network/tests/mockData/taskHistory.js b/frontend/src/network/tests/mockData/taskHistory.ts
similarity index 100%
rename from frontend/src/network/tests/mockData/taskHistory.js
rename to frontend/src/network/tests/mockData/taskHistory.ts
diff --git a/frontend/src/network/tests/mockData/tasksStats.js b/frontend/src/network/tests/mockData/tasksStats.ts
similarity index 100%
rename from frontend/src/network/tests/mockData/tasksStats.js
rename to frontend/src/network/tests/mockData/tasksStats.ts
diff --git a/frontend/src/network/tests/mockData/teams.js b/frontend/src/network/tests/mockData/teams.ts
similarity index 100%
rename from frontend/src/network/tests/mockData/teams.js
rename to frontend/src/network/tests/mockData/teams.ts
diff --git a/frontend/src/network/tests/mockData/userList.js b/frontend/src/network/tests/mockData/userList.ts
similarity index 100%
rename from frontend/src/network/tests/mockData/userList.js
rename to frontend/src/network/tests/mockData/userList.ts
diff --git a/frontend/src/network/tests/mockData/userStats.js b/frontend/src/network/tests/mockData/userStats.ts
similarity index 100%
rename from frontend/src/network/tests/mockData/userStats.js
rename to frontend/src/network/tests/mockData/userStats.ts
diff --git a/frontend/src/network/tests/server-handlers.js b/frontend/src/network/tests/server-handlers.ts
similarity index 95%
rename from frontend/src/network/tests/server-handlers.js
rename to frontend/src/network/tests/server-handlers.ts
index a17e768062..08769687eb 100644
--- a/frontend/src/network/tests/server-handlers.js
+++ b/frontend/src/network/tests/server-handlers.ts
@@ -1,4 +1,4 @@
-import { rest } from 'msw';
+import { DefaultBodyType, PathParams, ResponseComposition, rest, RestContext, RestRequest } from 'msw';
import {
getProjectSummary,
@@ -117,10 +117,12 @@ const handlers = [
return res(ctx.json(userFavorite));
}),
rest.post(API_URL + 'projects/:id/favorite/', async (req, res, ctx) => {
- return res(ctx.json(favoritePost(req.params.id)));
+ // return res(ctx.json(favoritePost(req.params.id)));
+ return res(ctx.json(favoritePost()));
}),
rest.get(API_URL + 'projects/:id/statistics/', async (req, res, ctx) => {
const { id } = req.params;
+ // @ts-expect-error TS Migrations
return res(ctx.json(getProjectStats(id)));
}),
rest.get(API_URL + 'projects/:id/contributions/queries/day/', async (req, res, ctx) => {
@@ -299,14 +301,16 @@ const handlers = [
rest.get(API_URL + 'interests/:id/', (req, res, ctx) => {
return res(ctx.json(interest));
}),
- rest.patch(API_URL + 'interests/:id', (req, res, ctx) => {
- return res(interestUpdationSuccess(req.body.name));
+ rest.patch(API_URL + 'interests/:id', async (req, res, ctx) => {
+ const body = await req.json();
+ return res(ctx.json(interestUpdationSuccess(body.name)));
}),
rest.delete(API_URL + 'interests/:id', (req, res, ctx) => {
return res(ctx.json(interestDeletionSuccess));
}),
- rest.post(API_URL + 'interests', (req, res, ctx) => {
- return res(ctx.json(interestCreationSuccess(req.body.name)));
+ rest.post(API_URL + 'interests', async (req, res, ctx) => {
+ const body = await req.json();
+ return res(ctx.json(interestCreationSuccess(body.name)));
}),
rest.get(API_URL + 'countries', (req, res, ctx) => {
return res(ctx.json(countries));
@@ -375,7 +379,7 @@ const handlers = [
}),
];
-const failedToConnectError = (req, res, ctx) => {
+const failedToConnectError = (_req: RestRequest>, res: ResponseComposition, _ctx: RestContext) => {
return res.networkError('Failed to connect');
};
diff --git a/frontend/src/network/tests/server.js b/frontend/src/network/tests/server.ts
similarity index 100%
rename from frontend/src/network/tests/server.js
rename to frontend/src/network/tests/server.ts
diff --git a/frontend/src/network/user.js b/frontend/src/network/user.ts
similarity index 64%
rename from frontend/src/network/user.js
rename to frontend/src/network/user.ts
index f2f71c3386..0dbc3f9e20 100644
--- a/frontend/src/network/user.js
+++ b/frontend/src/network/user.ts
@@ -1,7 +1,7 @@
import { API_URL } from '../config';
-export function postNewUser(user) {
- return fetch(`${API_URL}users/actions/register/`, {
+export async function postNewUser(user: unknown) {
+ return await fetch(`${API_URL}users/actions/register/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
diff --git a/frontend/src/routes.js b/frontend/src/routes.tsx
similarity index 100%
rename from frontend/src/routes.js
rename to frontend/src/routes.tsx
diff --git a/frontend/src/service-worker.js b/frontend/src/service-worker.js
index 527ba85f35..5d9df68021 100644
--- a/frontend/src/service-worker.js
+++ b/frontend/src/service-worker.js
@@ -47,7 +47,7 @@ registerRoute(
return true;
},
- createHandlerBoundToURL(process.env.PUBLIC_URL + '/index.html'),
+ createHandlerBoundToURL(import.meta.env.PUBLIC_URL + '/index.html'),
);
// An example runtime caching route for requests that aren't handled by the
diff --git a/frontend/src/serviceWorkerRegistration.js b/frontend/src/serviceWorkerRegistration.js
index c0cc74e587..c961109121 100644
--- a/frontend/src/serviceWorkerRegistration.js
+++ b/frontend/src/serviceWorkerRegistration.js
@@ -12,16 +12,16 @@
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
- // [::1] is the IPv6 localhost address.
- window.location.hostname === '[::1]' ||
- // 127.0.0.0/8 are considered localhost for IPv4.
- window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/),
+ // [::1] is the IPv6 localhost address.
+ window.location.hostname === '[::1]' ||
+ // 127.0.0.0/8 are considered localhost for IPv4.
+ window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/),
);
export function register(config) {
- if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
+ if (import.meta.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
- const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
+ const publicUrl = new URL(import.meta.env.PUBLIC_URL, window.location.href);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
@@ -30,7 +30,7 @@ export function register(config) {
}
window.addEventListener('load', () => {
- const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
+ const swUrl = `${import.meta.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
// This is running on localhost. Let's check if a service worker still exists or not.
@@ -41,7 +41,7 @@ export function register(config) {
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
- 'worker. To learn more, visit https://cra.link/PWA',
+ 'worker. To learn more, visit https://cra.link/PWA',
);
});
} else {
@@ -69,7 +69,7 @@ function registerValidSW(swUrl, config) {
// content until all client tabs are closed.
console.log(
'New content is available and will be used when all ' +
- 'tabs for this page are closed. See https://cra.link/PWA.',
+ 'tabs for this page are closed. See https://cra.link/PWA.',
);
// Execute callback
diff --git a/frontend/src/store/actions/auth.js b/frontend/src/store/actions/auth.ts
similarity index 54%
rename from frontend/src/store/actions/auth.js
rename to frontend/src/store/actions/auth.ts
index ef38b19b94..46197fe5f1 100644
--- a/frontend/src/store/actions/auth.js
+++ b/frontend/src/store/actions/auth.ts
@@ -1,6 +1,8 @@
import { setItem, removeItem, getItem } from '../../utils/safe_storage';
import { pushToLocalJSONAPI, fetchLocalJSONAPI } from '../../network/genericJSONRequest';
import { setLoader } from './loader';
+import { Dispatch } from 'redux';
+import { RootStore } from '..';
export const types = {
REGISTER_USER: 'REGISTER_USER',
@@ -13,7 +15,7 @@ export const types = {
SET_TOKEN: 'SET_TOKEN',
SET_SESSION: 'SET_SESSION',
CLEAR_SESSION: 'CLEAR_SESSION',
-};
+} as const;
export function clearUserDetails() {
return {
@@ -38,7 +40,7 @@ export const updateUserEmail = (userDetails, token, relevant_fields) => (dispatc
});
};
-export const logout = () => (dispatch) => {
+export const logout = () => (dispatch: Dispatch) => {
removeItem('username');
removeItem('token');
removeItem('action');
@@ -89,7 +91,7 @@ export function updateSession(session) {
};
}
-export const setAuthDetails = (username, token, osm_oauth_token) => (dispatch) => {
+export const setAuthDetails = (username: string, token: string, osm_oauth_token: string) => (dispatch: Dispatch) => {
const encoded_token = btoa(token);
setItem('token', encoded_token);
setItem('username', username);
@@ -105,53 +107,53 @@ export const setAuthDetails = (username, token, osm_oauth_token) => (dispatch) =
// UPDATES OSM INFORMATION OF THE USER
export const setUserDetails =
- (username, encodedToken, update = false) =>
- (dispatch) => {
- // only trigger the loader if this function is not being triggered to update the user information
- if (!update) dispatch(setLoader(true));
- fetchLocalJSONAPI(`users/${username}/openstreetmap/`, encodedToken)
- .then((osmInfo) => dispatch(updateOSMInfo(osmInfo)))
- .catch((error) => {
- console.log(error);
- dispatch(setLoader(false));
- });
- // GET USER DETAILS
- fetchLocalJSONAPI(`users/queries/${username}/`, encodedToken)
- .then((userDetails) => {
- dispatch(updateUserDetails(userDetails));
- // GET USER ORGS INFO
- fetchLocalJSONAPI(
- `organisations/?omitManagerList=true&manager_user_id=${userDetails.id}`,
- encodedToken,
- )
- .then((orgs) =>
- dispatch(updateOrgsInfo(orgs.organisations.map((org) => org.organisationId))),
+ (username: string, encodedToken: string, update: boolean = false) =>
+ (dispatch: Dispatch) => {
+ // only trigger the loader if this function is not being triggered to update the user information
+ if (!update) dispatch(setLoader(true));
+ fetchLocalJSONAPI(`users/${username}/openstreetmap/`, encodedToken)
+ .then((osmInfo) => dispatch(updateOSMInfo(osmInfo)))
+ .catch((error) => {
+ console.log(error);
+ dispatch(setLoader(false));
+ });
+ // GET USER DETAILS
+ fetchLocalJSONAPI(`users/queries/${username}/`, encodedToken)
+ .then((userDetails) => {
+ dispatch(updateUserDetails(userDetails));
+ // GET USER ORGS INFO
+ fetchLocalJSONAPI(
+ `organisations/?omitManagerList=true&manager_user_id=${userDetails.id}`,
+ encodedToken,
)
- .catch((error) => dispatch(updateOrgsInfo([])));
- fetchLocalJSONAPI(
- `teams/?omitMemberList=true&team_role=PROJECT_MANAGER&member=${userDetails.id}`,
- encodedToken,
- )
- .then((teams) => dispatch(updatePMsTeams(teams.teams.map((team) => team.teamId))))
- .catch((error) => dispatch(updatePMsTeams([])));
- dispatch(setLoader(false));
- })
- .catch((error) => {
- if (error.message === 'InvalidToken') dispatch(logout());
- dispatch(setLoader(false));
- });
- };
+ .then((orgs) =>
+ dispatch(updateOrgsInfo(orgs.organisations.map((org) => org.organisationId))),
+ )
+ .catch((error) => dispatch(updateOrgsInfo([])));
+ fetchLocalJSONAPI(
+ `teams/?omitMemberList=true&team_role=PROJECT_MANAGER&member=${userDetails.id}`,
+ encodedToken,
+ )
+ .then((teams) => dispatch(updatePMsTeams(teams.teams.map((team) => team.teamId))))
+ .catch((error) => dispatch(updatePMsTeams([])));
+ dispatch(setLoader(false));
+ })
+ .catch((error) => {
+ if (error.message === 'InvalidToken') dispatch(logout());
+ dispatch(setLoader(false));
+ });
+ };
-export const getUserDetails = (state) => (dispatch) => {
- if (state.auth.userDetails.username) {
+export const getUserDetails = (state: RootStore) => (dispatch: Dispatch) => {
+ if (state.auth.userDetails?.username) {
dispatch(setUserDetails(state.auth.userDetails.username, state.auth.token));
}
};
export const pushUserDetails =
(userDetails, token, update = false) =>
- (dispatch) => {
- pushToLocalJSONAPI(`users/me/actions/set-user/`, userDetails, token, 'PATCH').then((data) =>
- dispatch(setUserDetails(getItem('username'), token, update)),
- );
- };
+ (dispatch) => {
+ pushToLocalJSONAPI(`users/me/actions/set-user/`, userDetails, token, 'PATCH').then((data) =>
+ dispatch(setUserDetails(getItem('username'), token, update)),
+ );
+ };
diff --git a/frontend/src/store/actions/editor.js b/frontend/src/store/actions/editor.ts
similarity index 88%
rename from frontend/src/store/actions/editor.js
rename to frontend/src/store/actions/editor.ts
index cae5b7376f..7784e83d3a 100644
--- a/frontend/src/store/actions/editor.js
+++ b/frontend/src/store/actions/editor.ts
@@ -1,4 +1,4 @@
export const types = {
SET_EDITOR: 'SET_EDITOR',
SET_RAPIDEDITOR: 'SET_RAPIDEDITOR',
-};
+} as const;
diff --git a/frontend/src/store/actions/loader.js b/frontend/src/store/actions/loader.ts
similarity index 67%
rename from frontend/src/store/actions/loader.js
rename to frontend/src/store/actions/loader.ts
index 8c44e9343c..0c1588319b 100644
--- a/frontend/src/store/actions/loader.js
+++ b/frontend/src/store/actions/loader.ts
@@ -1,8 +1,8 @@
export const types = {
SET_LOADER: 'SET_LOADER',
-};
+} as const;
-export function setLoader(isLoading) {
+export function setLoader(isLoading: boolean) {
return {
type: types.SET_LOADER,
isLoading: isLoading,
diff --git a/frontend/src/store/actions/notifications.js b/frontend/src/store/actions/notifications.ts
similarity index 95%
rename from frontend/src/store/actions/notifications.js
rename to frontend/src/store/actions/notifications.ts
index de011f3e57..a0b6f30ae4 100644
--- a/frontend/src/store/actions/notifications.js
+++ b/frontend/src/store/actions/notifications.ts
@@ -4,4 +4,4 @@ export const types = {
NOTIFICATIONS_FAILURE: 'NOTIFICATIONS_FAILURE',
SET_UNREAD_COUNT: 'SET_UNREAD_COUNT',
DECREMENT_UNREAD_COUNT: 'DECREMENT_UNREAD_COUNT',
-};
+} as const;
diff --git a/frontend/src/store/actions/project.js b/frontend/src/store/actions/project.ts
similarity index 66%
rename from frontend/src/store/actions/project.js
rename to frontend/src/store/actions/project.ts
index a6bec46e4b..731a99c124 100644
--- a/frontend/src/store/actions/project.js
+++ b/frontend/src/store/actions/project.ts
@@ -1,9 +1,11 @@
+import { Dispatch } from "redux";
+
export const types = {
CREATE_PROJECT: 'CREATE_PROJECT',
SET_ID: 'SET_ID',
-};
+} as const;
-export const createProject = (params) => (dispatch) => {
+export const createProject = (params: any) => (dispatch: Dispatch) => {
const dispatch_params = {
type: types.CREATE_PROJECT,
params: params,
diff --git a/frontend/src/store/actions/tasks.js b/frontend/src/store/actions/tasks.ts
similarity index 64%
rename from frontend/src/store/actions/tasks.js
rename to frontend/src/store/actions/tasks.ts
index 91835e81c0..81f0f18862 100644
--- a/frontend/src/store/actions/tasks.js
+++ b/frontend/src/store/actions/tasks.ts
@@ -1,25 +1,27 @@
+import { Dispatch } from "redux";
+
export const types = {
SET_LOCKED_TASKS: 'SET_LOCKED_TASKS',
SET_PROJECT: 'SET_PROJECT',
SET_TASKS_STATUS: 'SET_TASKS_STATUS',
CLEAR_LOCKED_TASKS: 'CLEAR_LOCKED_TASKS',
-};
+} as const;
-export function updateProject(project) {
+export function updateProject(project: any) {
return {
type: types.SET_PROJECT,
project: project,
};
}
-export function updateLockedTasks(tasks) {
+export function updateLockedTasks(tasks: any) {
return {
type: types.SET_LOCKED_TASKS,
tasks: tasks,
};
}
-export function updateTasksStatus(status) {
+export function updateTasksStatus(status: any) {
return {
type: types.SET_TASKS_STATUS,
status: status,
@@ -32,11 +34,11 @@ export function clearLockedTasks() {
};
}
-export const setLockedTasks = (tasks, projectId) => (dispatch) => {
+export const setLockedTasks = (tasks, projectId) => (dispatch: Dispatch) => {
dispatch(updateLockedTasks(tasks));
dispatch(updateProject(projectId));
};
-export const setTasksStatus = (status) => (dispatch) => {
+export const setTasksStatus = (status) => (dispatch: Dispatch) => {
dispatch(updateTasksStatus(status));
};
diff --git a/frontend/src/store/actions/user.js b/frontend/src/store/actions/user.ts
similarity index 64%
rename from frontend/src/store/actions/user.js
rename to frontend/src/store/actions/user.ts
index e493cf2e8a..c0f41aaeba 100644
--- a/frontend/src/store/actions/user.js
+++ b/frontend/src/store/actions/user.ts
@@ -1,9 +1,10 @@
import { setItem } from '../../utils/safe_storage';
import { postNewUser } from '../../network/user';
import { types } from './auth';
+import { Dispatch } from 'redux';
-export const registerUser = (postData) => (dispatch) => {
- return postNewUser(postData).then((res) => {
+export const registerUser = (postData: unknown) => async (dispatch: Dispatch) => {
+ return await postNewUser(postData).then((res) => {
if (res.success === true) {
setItem('userId', res.id);
}
diff --git a/frontend/src/store/actions/userPreferences.js b/frontend/src/store/actions/userPreferences.ts
similarity index 69%
rename from frontend/src/store/actions/userPreferences.js
rename to frontend/src/store/actions/userPreferences.ts
index f7995c303b..68994d79bc 100644
--- a/frontend/src/store/actions/userPreferences.js
+++ b/frontend/src/store/actions/userPreferences.ts
@@ -1,3 +1,4 @@
+import { Dispatch } from 'redux';
import { setItem } from '../../utils/safe_storage';
export const types = {
@@ -6,16 +7,16 @@ export const types = {
TOGGLE_MAP: 'TOGGLE_MAP',
TOGGLE_LIST_VIEW: 'TOGGLE_LIST_VIEW',
TOGGLE_CARD_VIEW: 'TOGGLE_CARD_VIEW',
-};
+} as const;
-export function updateLocale(locale) {
+export function updateLocale(locale: string) {
return {
type: types.SET_LOCALE,
locale: locale,
};
}
-export const setLocale = (locale) => (dispatch) => {
+export const setLocale = (locale: string) => (dispatch: Dispatch) => {
setItem('locale', locale);
dispatch(updateLocale(locale));
};
diff --git a/frontend/src/store/index.js b/frontend/src/store/index.ts
similarity index 52%
rename from frontend/src/store/index.js
rename to frontend/src/store/index.ts
index 3e798a3fdd..3c120072fa 100644
--- a/frontend/src/store/index.js
+++ b/frontend/src/store/index.ts
@@ -6,6 +6,7 @@ import storage from 'redux-persist/lib/storage';
import { setItem } from '../utils/safe_storage';
import reducers from './reducers';
+import { composeWithDevTools } from '@redux-devtools/extension';
const persistConfig = {
key: 'root',
@@ -15,22 +16,16 @@ const persistConfig = {
const persistedReducer = persistReducer(persistConfig, reducers);
-const enhancers = [];
+const store = createStore(persistedReducer, undefined, composeWithDevTools((applyMiddleware(thunk))));
-const composeEnhancers =
- (process.env.NODE_ENV === 'development' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) ||
- compose;
-
-const composedEnhancers = composeEnhancers(applyMiddleware(thunk), ...enhancers);
-
-const store = createStore(persistedReducer, {}, composedEnhancers);
+export type RootStore = ReturnType;
const persistor = persistStore(store);
store.subscribe(() => {
- setItem('mapShown', store.getState().preferences['mapShown']);
- setItem('action', store.getState().preferences['action']);
- setItem('projectListView', store.getState().preferences['projectListView']);
+ setItem('mapShown', store.getState().preferences.mapShown);
+ setItem('action', store.getState().preferences.action);
+ setItem('projectListView', store.getState().preferences.projectListView);
});
export { store, persistor };
diff --git a/frontend/src/store/reducers/auth.js b/frontend/src/store/reducers/auth.js
deleted file mode 100644
index 30656f6b40..0000000000
--- a/frontend/src/store/reducers/auth.js
+++ /dev/null
@@ -1,38 +0,0 @@
-import { types } from '../actions/auth';
-
-const initialState = {
- userDetails: {},
- token: '',
- session: {},
- osm: {},
- organisations: [],
- pmTeams: [],
-};
-
-export function authorizationReducer(state = initialState, action) {
- switch (action.type) {
- case types.SET_USER_DETAILS: {
- return { ...state, userDetails: action.userDetails };
- }
- case types.SET_OSM: {
- return { ...state, osm: action.osm };
- }
- case types.SET_ORGANISATIONS: {
- return { ...state, organisations: action.organisations };
- }
- case types.SET_PM_TEAMS: {
- return { ...state, pmTeams: action.teams };
- }
- case types.SET_TOKEN: {
- return { ...state, token: action.token };
- }
- case types.SET_SESSION: {
- return { ...state, session: action.session };
- }
- case types.CLEAR_SESSION: {
- return initialState;
- }
- default:
- return state;
- }
-}
diff --git a/frontend/src/store/reducers/auth.ts b/frontend/src/store/reducers/auth.ts
new file mode 100644
index 0000000000..4817374d27
--- /dev/null
+++ b/frontend/src/store/reducers/auth.ts
@@ -0,0 +1,80 @@
+import { types } from '../actions/auth';
+
+type UserData = {
+ userDetails: {
+ id: number;
+ username: string;
+ emailAddress: string;
+ role: string;
+ pictureUrl: string;
+ } | null,
+ token: string,
+ session: {
+ osm_oauth_token: string;
+ } | null,
+ osm: {
+ accountCreated: string;
+ } | null,
+ organisations: string[],
+ pmTeams: string[],
+}
+
+const initialState = {
+ userDetails: null,
+ token: '',
+ session: null,
+ osm: null,
+ organisations: [],
+ pmTeams: [],
+} satisfies UserData;
+
+type Actions = {
+ type: typeof types.SET_USER_DETAILS,
+ userDetails: UserData['userDetails'],
+} | {
+ type: typeof types.SET_OSM,
+ osm: UserData['osm'],
+} | {
+ type: typeof types.SET_ORGANISATIONS,
+ organisations: UserData['organisations'],
+} | {
+ type: typeof types.SET_PM_TEAMS,
+ teams: UserData['pmTeams'],
+} | {
+ type: typeof types.SET_TOKEN,
+ token: UserData['token'],
+} | {
+ type: typeof types.SET_SESSION,
+ session: UserData['session'],
+} | {
+ type: typeof types.CLEAR_SESSION,
+ session: null,
+}
+
+export function authorizationReducer(state: UserData = initialState, action: Actions): UserData {
+ switch (action.type) {
+ case types.SET_USER_DETAILS: {
+ return { ...state, userDetails: action.userDetails };
+ }
+ case types.SET_OSM: {
+ return { ...state, osm: action.osm };
+ }
+ case types.SET_ORGANISATIONS: {
+ return { ...state, organisations: action.organisations };
+ }
+ case types.SET_PM_TEAMS: {
+ return { ...state, pmTeams: action.teams };
+ }
+ case types.SET_TOKEN: {
+ return { ...state, token: action.token };
+ }
+ case types.SET_SESSION: {
+ return { ...state, session: action.session };
+ }
+ case types.CLEAR_SESSION: {
+ return initialState;
+ }
+ default:
+ return state;
+ }
+}
diff --git a/frontend/src/store/reducers/editor.js b/frontend/src/store/reducers/editor.ts
similarity index 56%
rename from frontend/src/store/reducers/editor.js
rename to frontend/src/store/reducers/editor.ts
index 7f428b676b..e0a38fafbe 100644
--- a/frontend/src/store/reducers/editor.js
+++ b/frontend/src/store/reducers/editor.ts
@@ -1,11 +1,24 @@
import { types } from '../actions/editor';
+type EditorState = {
+ context: any;
+ rapidContext: { context: any; dom: any };
+};
+
const initialState = {
context: null,
rapidContext: { context: null, dom: null },
-};
+} satisfies EditorState;
+
+type Actions = {
+ type: typeof types.SET_EDITOR;
+ context: any;
+} | {
+ type: typeof types.SET_RAPIDEDITOR;
+ context: { context: any; dom: any };
+}
-export function editorReducer(state = initialState, action) {
+export function editorReducer(state = initialState, action: Actions) {
switch (action.type) {
case types.SET_EDITOR: {
return {
diff --git a/frontend/src/store/reducers/index.js b/frontend/src/store/reducers/index.ts
similarity index 92%
rename from frontend/src/store/reducers/index.js
rename to frontend/src/store/reducers/index.ts
index 67c0203dd9..505ecdb1c0 100644
--- a/frontend/src/store/reducers/index.js
+++ b/frontend/src/store/reducers/index.ts
@@ -10,7 +10,7 @@ import { loaderReducer } from './loader';
import { editorReducer } from './editor';
import { orgBarVisibilityReducer } from './orgTopBar';
-export default combineReducers({
+const reducers = combineReducers({
preferences: preferencesReducer,
auth: authorizationReducer,
notifications: notificationsReducer,
@@ -21,3 +21,5 @@ export default combineReducers({
editor: editorReducer,
orgBarVisibility: orgBarVisibilityReducer,
});
+
+export default reducers;
diff --git a/frontend/src/store/reducers/loader.js b/frontend/src/store/reducers/loader.ts
similarity index 52%
rename from frontend/src/store/reducers/loader.js
rename to frontend/src/store/reducers/loader.ts
index 6e1675cade..2484edd857 100644
--- a/frontend/src/store/reducers/loader.js
+++ b/frontend/src/store/reducers/loader.ts
@@ -2,9 +2,18 @@ import { types } from '../actions/loader';
const initialState = {
isLoading: false,
-};
+} satisfies { isLoading: boolean; };
-export function loaderReducer(state = initialState, action) {
+type Actions = {
+ type: typeof types.SET_LOADER;
+ isLoading: boolean;
+}
+
+export function loaderReducer(state: {
+ isLoading: boolean;
+} = initialState, action: Actions): {
+ isLoading: boolean;
+} {
switch (action.type) {
case types.SET_LOADER: {
return {
diff --git a/frontend/src/store/reducers/notifications.js b/frontend/src/store/reducers/notifications.ts
similarity index 52%
rename from frontend/src/store/reducers/notifications.js
rename to frontend/src/store/reducers/notifications.ts
index da05d9e5dc..5504d0e468 100644
--- a/frontend/src/store/reducers/notifications.js
+++ b/frontend/src/store/reducers/notifications.ts
@@ -1,12 +1,52 @@
import { types } from '../actions/notifications';
+type Notification = {
+ notifications: any[];
+ userMessages: any[];
+ isLoading: boolean;
+ isFirstLoading: boolean;
+ isError: boolean;
+ unreadNotificationsMini: any[];
+ lastParams: any;
+ pagination: { hasNext: boolean; hasPrev: boolean; page: number };
+ unreadCount: number;
+ params: {
+ status: string;
+ }
+};
+
const initialState = {
notifications: [],
+ userMessages: [],
+ unreadNotificationsMini: [],
+ isLoading: false,
+ isFirstLoading: false,
+ isError: false,
+ lastParams: {},
pagination: { hasNext: false, hasPrev: false, page: 1 },
- unreadCount: null,
+ unreadCount: 0,
+ params: {
+ status: ""
+ }
+} satisfies Notification;
+
+type Actions = {
+ type: typeof types.NOTIFICATIONS_INIT;
+} | {
+ type: typeof types.NOTIFICATIONS_SUCCESS;
+ userMessages: any[];
+ pagination: Notification["pagination"]
+ params: any;
+} | {
+ type: typeof types.NOTIFICATIONS_FAILURE;
+} | {
+ type: typeof types.SET_UNREAD_COUNT;
+ payload: number;
+} | {
+ type: typeof types.DECREMENT_UNREAD_COUNT;
};
-export const notificationsReducer = (state = initialState, action) => {
+export const notificationsReducer = (state: Notification = initialState, action: Actions): Notification => {
switch (action.type) {
case types.NOTIFICATIONS_INIT:
return {
@@ -16,14 +56,14 @@ export const notificationsReducer = (state = initialState, action) => {
isError: false,
};
case types.NOTIFICATIONS_SUCCESS:
- const pagedNotifs = action.payload.userMessages.map((n) => ({
+ const pagedNotifs = action.userMessages.map((n) => ({
...n,
- page: action.payload.pagination.page,
+ page: action.pagination.page,
}));
const goodForMiniResults =
action.params['status'] &&
action.params['status'] === 'unread' &&
- action.payload.pagination.page === 1;
+ action.pagination.page === 1;
return {
...state,
@@ -34,7 +74,7 @@ export const notificationsReducer = (state = initialState, action) => {
lastParams: action.params,
unreadNotificationsMini:
(goodForMiniResults && pagedNotifs) || state.unreadNotificationsMini,
- pagination: action.payload.pagination,
+ pagination: action.pagination,
};
case types.NOTIFICATIONS_FAILURE:
return {
diff --git a/frontend/src/store/reducers/orgTopBar.js b/frontend/src/store/reducers/orgTopBar.ts
similarity index 51%
rename from frontend/src/store/reducers/orgTopBar.js
rename to frontend/src/store/reducers/orgTopBar.ts
index c8845908df..051e6e203e 100644
--- a/frontend/src/store/reducers/orgTopBar.js
+++ b/frontend/src/store/reducers/orgTopBar.ts
@@ -1,8 +1,17 @@
const initialState = {
isVisible: true,
+} satisfies {
+ isVisible: boolean;
};
-export function orgBarVisibilityReducer(state = initialState, action) {
+type Actions = {
+ type: 'SET_VISIBILITY';
+ isVisible: boolean;
+}
+
+export function orgBarVisibilityReducer(state: {
+ isVisible: boolean;
+} = initialState, action: Actions) {
switch (action.type) {
case 'SET_VISIBILITY': {
return {
diff --git a/frontend/src/store/reducers/project.js b/frontend/src/store/reducers/project.ts
similarity index 53%
rename from frontend/src/store/reducers/project.js
rename to frontend/src/store/reducers/project.ts
index 7d64b37035..8b0617bfa1 100644
--- a/frontend/src/store/reducers/project.js
+++ b/frontend/src/store/reducers/project.ts
@@ -1,11 +1,24 @@
import { types } from '../actions/project';
+type ProjectState = {
+ projectId: string | null,
+ params: any,
+};
+
const initialState = {
projectId: null,
params: {},
+} satisfies ProjectState;
+
+type Actions = {
+ type: typeof types.CREATE_PROJECT
+ params: any
+} | {
+ type: typeof types.SET_ID,
+ projectId: string
};
-export function projectReducer(state = initialState, action) {
+export function projectReducer(state: ProjectState = initialState, action: Actions) {
switch (action.type) {
case types.CREATE_PROJECT: {
return { ...state, params: action.params };
diff --git a/frontend/src/store/reducers/tasks.js b/frontend/src/store/reducers/tasks.ts
similarity index 50%
rename from frontend/src/store/reducers/tasks.js
rename to frontend/src/store/reducers/tasks.ts
index ad7c3b5a20..fe3491fb67 100644
--- a/frontend/src/store/reducers/tasks.js
+++ b/frontend/src/store/reducers/tasks.ts
@@ -1,12 +1,36 @@
import { types } from '../actions/tasks';
+type TaskData = {
+ tasks: {
+ taskId: string;
+ title: string;
+ projectId: string;
+ status: string;
+ }[],
+ status: string | null,
+ project: any
+}
+
const initialState = {
project: null,
tasks: [],
status: null,
-};
+} satisfies TaskData;
+
+type Actions = {
+ type: typeof types.SET_PROJECT,
+ project: any
+} | {
+ type: typeof types.SET_LOCKED_TASKS,
+ tasks: any
+} | {
+ type: typeof types.SET_TASKS_STATUS,
+ status: string
+} | {
+ type: typeof types.CLEAR_LOCKED_TASKS
+}
-export function tasksReducer(state = initialState, action) {
+export function tasksReducer(state: TaskData = initialState, action: Actions): TaskData {
switch (action.type) {
case types.SET_PROJECT: {
return { ...state, project: action.project };
diff --git a/frontend/src/store/reducers/user.js b/frontend/src/store/reducers/user.ts
similarity index 53%
rename from frontend/src/store/reducers/user.js
rename to frontend/src/store/reducers/user.ts
index dc376ec31f..d994a19965 100644
--- a/frontend/src/store/reducers/user.js
+++ b/frontend/src/store/reducers/user.ts
@@ -1,5 +1,14 @@
import { types } from '../actions/auth';
+type User = {
+ user: {
+ email: string;
+ success: boolean;
+ details: string;
+ id: number;
+ }
+};
+
const initialState = {
user: {
email: '',
@@ -7,9 +16,14 @@ const initialState = {
details: '',
id: 0,
},
+} satisfies User;
+
+type Actions = {
+ type: typeof types.REGISTER_USER,
+ payload: User['user'],
};
-export function userReducer(state = initialState, action) {
+export function userReducer(state: User = initialState, action: Actions) {
switch (action.type) {
case types.REGISTER_USER: {
return {
diff --git a/frontend/src/store/reducers/userPreferences.js b/frontend/src/store/reducers/userPreferences.ts
similarity index 56%
rename from frontend/src/store/reducers/userPreferences.js
rename to frontend/src/store/reducers/userPreferences.ts
index 1bf1ea1394..095d8d0841 100644
--- a/frontend/src/store/reducers/userPreferences.js
+++ b/frontend/src/store/reducers/userPreferences.ts
@@ -1,13 +1,37 @@
import { types } from '../actions/userPreferences';
+type UserPreferences = {
+ locale: string,
+ mapShown: boolean,
+ projectListView: boolean,
+ action: string,
+}
+
const initialState = {
- locale: null,
+ locale: "en",
mapShown: false,
projectListView: false,
action: 'any',
-};
+} satisfies UserPreferences;
+
+type Actions = {
+ type: typeof types.SET_LOCALE;
+ locale: string;
+} | {
+ type: typeof types.SET_ACTION;
+ action: string;
+} | {
+ type: typeof types.TOGGLE_MAP;
+ mapShown: boolean;
+} | {
+ type: typeof types.TOGGLE_LIST_VIEW;
+ projectListView: boolean;
+} | {
+ type: typeof types.TOGGLE_CARD_VIEW;
+ projectListView: boolean;
+}
-export function preferencesReducer(state = initialState, action) {
+export function preferencesReducer(state: UserPreferences = initialState, action: Actions) {
switch (action.type) {
case types.SET_LOCALE: {
return {
diff --git a/frontend/src/utils/CommaArrayParam.js b/frontend/src/utils/CommaArrayParam.ts
similarity index 100%
rename from frontend/src/utils/CommaArrayParam.js
rename to frontend/src/utils/CommaArrayParam.ts
diff --git a/frontend/src/utils/chart.js b/frontend/src/utils/chart.ts
similarity index 50%
rename from frontend/src/utils/chart.js
rename to frontend/src/utils/chart.ts
index 29f9e472da..3974ea2b86 100644
--- a/frontend/src/utils/chart.js
+++ b/frontend/src/utils/chart.ts
@@ -1,24 +1,20 @@
import { enUS } from 'date-fns/locale';
import { formatISO } from 'date-fns';
-/**
- * x axis configuration common between this and {@link ../projectDetail/timeline.js}
- * @param unit The base unit for the axis
- * @typedef {import('chart.js').ScaleOptionsByType} ScaleOptionsByType
- * @returns {ScaleOptionsByType} The options to use for x axis configuration
- */
-function xAxisTimeSeries(unit) {
+import type { ScaleOptionsByType, TimeUnit } from "chart.js";
+
+function xAxisTimeSeries(unit: TimeUnit) {
return {
type: 'timeseries',
adapters: { date: { locale: enUS } },
time: {
unit: unit,
- tooltipFormat: enUS.formatLong.date,
+ tooltipFormat: enUS.formatLong.date({}),
},
ticks: {
source: 'labels',
callback: (value, index, ticks) => formatISO(ticks[index].value, { representation: 'date' }),
},
- };
+ } satisfies ScaleOptionsByType;
}
export { xAxisTimeSeries };
diff --git a/frontend/src/utils/date.js b/frontend/src/utils/date.ts
similarity index 96%
rename from frontend/src/utils/date.js
rename to frontend/src/utils/date.ts
index 1ab5170b51..865035b439 100644
--- a/frontend/src/utils/date.js
+++ b/frontend/src/utils/date.ts
@@ -8,7 +8,7 @@ import {
endOfYear,
} from 'date-fns';
-export function getPastMonths(months) {
+export function getPastMonths(months: number) {
let today = new Date();
return today.setMonth(today.getMonth() - months);
}
diff --git a/frontend/src/utils/formatChartJSData.js b/frontend/src/utils/formatChartJSData.ts
similarity index 83%
rename from frontend/src/utils/formatChartJSData.js
rename to frontend/src/utils/formatChartJSData.ts
index b352de49c6..2b48418094 100644
--- a/frontend/src/utils/formatChartJSData.js
+++ b/frontend/src/utils/formatChartJSData.ts
@@ -1,4 +1,4 @@
-export const formatChartData = (reference, stats) => {
+export const formatChartData = (reference: any, stats: any) => {
let data = { datasets: [{ data: [], backgroundColor: [] }], labels: [] };
data.datasets[0].data = reference.map((f) => stats[f.field]);
@@ -11,7 +11,7 @@ export const formatChartData = (reference, stats) => {
return data;
};
-export const formatTimelineData = (stats, mappedTasksConfig, validatedTasksConfig) => {
+export const formatTimelineData = (stats: any, mappedTasksConfig: any, validatedTasksConfig: any) => {
let mapped = {
data: [],
backgroundColor: mappedTasksConfig.color,
@@ -38,7 +38,7 @@ export const formatTimelineData = (stats, mappedTasksConfig, validatedTasksConfi
return { datasets: [validated, mapped], labels: labels };
};
-export const formatTasksStatsData = (stats, mappedTasksConfig, validatedTasksConfig) => {
+export const formatTasksStatsData = (stats: any, mappedTasksConfig: any, validatedTasksConfig: any) => {
let mapped = {
data: [],
backgroundColor: mappedTasksConfig.color,
@@ -57,7 +57,7 @@ export const formatTasksStatsData = (stats, mappedTasksConfig, validatedTasksCon
return { datasets: [mapped, validated], labels: labels };
};
-export const formatTooltip = (context) => {
+export const formatTooltip = (context: any) => {
var label = context.label;
if (label) label += ': ';
label += context.dataset.data[context.dataIndex];
@@ -65,7 +65,7 @@ export const formatTooltip = (context) => {
return `${label}%`;
};
-export const formatTimelineTooltip = (context, isPercent) => {
+export const formatTimelineTooltip = (context: any, isPercent: boolean) => {
var label = context.dataset.label || '';
if (label) label += ': ';
label += context.dataset.data[context.dataIndex];
diff --git a/frontend/src/utils/formattedRelativeTime.js b/frontend/src/utils/formattedRelativeTime.tsx
similarity index 70%
rename from frontend/src/utils/formattedRelativeTime.js
rename to frontend/src/utils/formattedRelativeTime.tsx
index 58cfd3b1ed..dd24432d86 100644
--- a/frontend/src/utils/formattedRelativeTime.js
+++ b/frontend/src/utils/formattedRelativeTime.tsx
@@ -2,7 +2,9 @@ import { FormattedRelativeTime } from 'react-intl';
import { selectUnit } from './selectUnit';
-export const RelativeTimeWithUnit = ({ date }) => {
+export const RelativeTimeWithUnit = ({ date }: {
+ date: string | number | Date;
+}) => {
const { value, unit } = selectUnit(new Date(date));
return ;
diff --git a/frontend/src/utils/htmlFromMarkdown.js b/frontend/src/utils/htmlFromMarkdown.ts
similarity index 90%
rename from frontend/src/utils/htmlFromMarkdown.js
rename to frontend/src/utils/htmlFromMarkdown.ts
index 5f2ef5acf8..c9ef02c66c 100644
--- a/frontend/src/utils/htmlFromMarkdown.js
+++ b/frontend/src/utils/htmlFromMarkdown.ts
@@ -3,7 +3,7 @@ import DOMPurify from 'dompurify';
const VIDEO_TAG_REGEXP = new RegExp(/^::youtube\[(.*)\]$/);
-const parseMarkdown = (markdownText) => {
+const parseMarkdown = async (markdownText: string) => {
marked.use({
gfm: false,
extensions: [
@@ -47,11 +47,11 @@ const parseMarkdown = (markdownText) => {
},
],
});
- return marked.parse(markdownText);
+ return await marked.parse(markdownText);
};
/* per https://stackoverflow.com/a/34688574/272018 */
-export const htmlFromMarkdown = (markdownText) => {
+export const htmlFromMarkdown = async (markdownText: string) => {
DOMPurify.addHook('afterSanitizeAttributes', function (node) {
// set all elements owning target to target=_blank
if ('target' in node) {
@@ -81,10 +81,10 @@ export const htmlFromMarkdown = (markdownText) => {
ADD_ATTR: ['allow', 'allowfullscreen', 'frameborder'],
};
- return { __html: DOMPurify.sanitize(parseMarkdown(markdownText), config) };
+ return { __html: DOMPurify.sanitize(await parseMarkdown(markdownText), config) };
};
-export const formatUserNamesToLink = (text) => {
+export const formatUserNamesToLink = (text: string) => {
const regex = /@\[([^\]]+)\]/gi;
// Find usernames with a regular expression. They all start with '[@' and end with ']'
const usernames = text && text.match(regex);
diff --git a/frontend/src/utils/internationalization.js b/frontend/src/utils/internationalization.jsx
similarity index 100%
rename from frontend/src/utils/internationalization.js
rename to frontend/src/utils/internationalization.jsx
diff --git a/frontend/src/utils/login.js b/frontend/src/utils/login.ts
similarity index 100%
rename from frontend/src/utils/login.js
rename to frontend/src/utils/login.ts
diff --git a/frontend/src/utils/promise.js b/frontend/src/utils/promise.ts
similarity index 91%
rename from frontend/src/utils/promise.js
rename to frontend/src/utils/promise.ts
index a9af63507a..81208988b2 100644
--- a/frontend/src/utils/promise.js
+++ b/frontend/src/utils/promise.ts
@@ -1,4 +1,4 @@
-export function cancelablePromise(promise: Promise<*>) {
+export function cancelablePromise(promise: Promise) {
let hasCanceled_ = false;
const wrappedPromise = new Promise((resolve, reject) => {
@@ -39,6 +39,6 @@ export function cancelableFetchJSON(url: string) {
);
}
-export function delayPromise(interval: number): { promise: Promise<*>, cancel: () => any } {
+export function delayPromise(interval: number): { promise: Promise, cancel: () => any } {
return cancelablePromise(new Promise((res) => setTimeout(res, interval)));
}
diff --git a/frontend/src/utils/random.js b/frontend/src/utils/random.ts
similarity index 65%
rename from frontend/src/utils/random.js
rename to frontend/src/utils/random.ts
index adc5c6a557..e35e66aaa7 100644
--- a/frontend/src/utils/random.js
+++ b/frontend/src/utils/random.ts
@@ -1,9 +1,9 @@
-export function getRandomInt(min, max) {
+export function getRandomInt(min: number, max: number) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min; //The maximum is exclusive and the minimum is inclusive
}
-export function getRandomArrayItem(arr) {
+export function getRandomArrayItem(arr: unknown[]) {
return arr[getRandomInt(0, arr.length)];
}
diff --git a/frontend/src/utils/remapParamsToAPI.js b/frontend/src/utils/remapParamsToAPI.ts
similarity index 78%
rename from frontend/src/utils/remapParamsToAPI.js
rename to frontend/src/utils/remapParamsToAPI.ts
index ba1df405fc..c0c3e311cc 100644
--- a/frontend/src/utils/remapParamsToAPI.js
+++ b/frontend/src/utils/remapParamsToAPI.ts
@@ -1,4 +1,4 @@
-const fromEntries = require('fromentries');
+import fromEntries from "fromentries"
/* read about it at MDN's Object.fromEntries – this is a tiny polyfill/ponyfill.
/* The API uses slightly different JSON keys than the queryParams,
@@ -8,9 +8,10 @@ const fromEntries = require('fromentries');
left key is client query param, right value is what backend calls the parameter
*/
-export const remapParamsToAPI = (param, conversion) => {
- function mapObject(obj, fn) {
+export const remapParamsToAPI = (param: unknown, conversion: unknown) => {
+ function mapObject(obj: unknown, fn: (n: any) => void) {
/* like Object.fromEntries */
+ // @ts-expect-error TS migration
return fromEntries(Object.entries(obj).map(fn));
}
const remapped = mapObject(param, (n) => {
diff --git a/frontend/src/utils/safe_storage.js b/frontend/src/utils/safe_storage.ts
similarity index 73%
rename from frontend/src/utils/safe_storage.js
rename to frontend/src/utils/safe_storage.ts
index 761da61f7f..0bee4dab0a 100644
--- a/frontend/src/utils/safe_storage.js
+++ b/frontend/src/utils/safe_storage.ts
@@ -6,9 +6,13 @@
* Wraps localStorage.getItem in a try/catch. Return null
* if the key does not exist or localStorage fails.
*/
-function getItem(key: string): ?string {
+function getItem(key: string) {
try {
- return localStorage.getItem(key) || null;
+ const gotKey = localStorage.getItem(key);
+ if (gotKey === null) {
+ return null;
+ }
+ return JSON.parse(gotKey) as unknown;
} catch (err) {
console.warn('Could not read from localStorage.');
return null;
@@ -18,9 +22,9 @@ function getItem(key: string): ?string {
/**
* Wraps localStorage.setItem in a try/catch.
*/
-function setItem(key: string, value: string): void {
+function setItem(key: string, value: unknown): void {
try {
- localStorage.setItem(key, value);
+ localStorage.setItem(key, JSON.stringify(value));
} catch (err) {
console.warn('Could not write to localStorage.');
}
diff --git a/frontend/src/utils/selectUnit.js b/frontend/src/utils/selectUnit.ts
similarity index 81%
rename from frontend/src/utils/selectUnit.js
rename to frontend/src/utils/selectUnit.ts
index a40ef56989..39da06b165 100644
--- a/frontend/src/utils/selectUnit.js
+++ b/frontend/src/utils/selectUnit.ts
@@ -1,10 +1,15 @@
// this code is an adaptation of https://www.npmjs.com/package/@formatjs/intl-utils
-var __assign =
+import { FormattedRelativeTime } from "react-intl";
+
+let __assign =
+ // @ts-expect-error TS Migrations
(this && this.__assign) ||
function () {
+ // @ts-expect-error TS Migrations
__assign =
Object.assign ||
+ // @ts-expect-error TS Migrations
function (t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
@@ -27,13 +32,18 @@ const DEFAULT_THRESHOLDS = {
day: 7,
};
-export function selectUnit(from, to, thresholds) {
+export function selectUnit(from: Date | number, to: Date | number = Date.now(), thresholds = DEFAULT_THRESHOLDS): {
+ value: number;
+ unit: Parameters["0"]["unit"]
+} {
if (to === void 0) {
to = Date.now();
}
if (thresholds === void 0) {
+ // @ts-expect-error TS Migrations
thresholds = {};
}
+ // @ts-expect-error TS Migrations
const resolvedThresholds = __assign(__assign({}, DEFAULT_THRESHOLDS), thresholds || {});
const secs = (+from - +to) / MS_PER_SECOND;
if (Math.abs(secs) < resolvedThresholds.second) {
diff --git a/frontend/src/utils/slugifyFileName.js b/frontend/src/utils/slugifyFileName.ts
similarity index 80%
rename from frontend/src/utils/slugifyFileName.js
rename to frontend/src/utils/slugifyFileName.ts
index bb5e69484b..19d70f42d3 100644
--- a/frontend/src/utils/slugifyFileName.js
+++ b/frontend/src/utils/slugifyFileName.ts
@@ -1,6 +1,6 @@
import slug from 'slug';
-export const slugifyFileName = (name, mimetype) => {
+export const slugifyFileName = (name: string, mimetype: string) => {
// slugify file names in order to avoid problems on the markdown
if (name.lastIndexOf('.') === -1) {
name = `${name}.${mimetype.split('/')[1]}`;
diff --git a/frontend/src/utils/sorting.js b/frontend/src/utils/sorting.js
deleted file mode 100644
index 6c36a2b27c..0000000000
--- a/frontend/src/utils/sorting.js
+++ /dev/null
@@ -1,19 +0,0 @@
-export function compareTaskId(a, b) {
- if (a.properties.taskId > b.properties.taskId) return 1;
- if (b.properties.taskId > a.properties.taskId) return -1;
- return 0;
-}
-
-export function compareLastUpdate(a, b) {
- return new Date(b.properties.actionDate) - new Date(a.properties.actionDate);
-}
-
-export function compareHistoryLastUpdate(a, b) {
- return new Date(b.actionDate) - new Date(a.actionDate);
-}
-
-export function compareByPropertyDescending(a, b, property) {
- if (a[property] > b[property]) return -1;
- if (b[property] > a[property]) return 1;
- return 0;
-}
diff --git a/frontend/src/utils/sorting.ts b/frontend/src/utils/sorting.ts
new file mode 100644
index 0000000000..0e56ac5f79
--- /dev/null
+++ b/frontend/src/utils/sorting.ts
@@ -0,0 +1,41 @@
+import { differenceInDays } from "date-fns";
+
+export function compareTaskId(a: {
+ properties: {
+ taskId: number;
+ }
+}, b: {
+ properties: {
+ taskId: number;
+ }
+}) {
+ if (a.properties.taskId > b.properties.taskId) return 1;
+ if (b.properties.taskId > a.properties.taskId) return -1;
+ return 0;
+}
+
+export function compareLastUpdate(a: {
+ properties: {
+ actionDate: string | number | Date;
+ }
+}, b: {
+ properties: {
+ actionDate: string | number | Date;
+ }
+}) {
+ return differenceInDays(new Date(a.properties.actionDate), new Date(b.properties.actionDate))
+}
+
+export function compareHistoryLastUpdate(a: {
+ actionDate: string | number | Date;
+}, b: {
+ actionDate: string | number | Date;
+}) {
+ return differenceInDays(new Date(b.actionDate), new Date(a.actionDate));
+}
+
+export function compareByPropertyDescending(a: any[], b: any[], property: any) {
+ if (a[property] > b[property]) return -1;
+ if (b[property] > a[property]) return 1;
+ return 0;
+}
diff --git a/frontend/src/utils/testWithIntl.js b/frontend/src/utils/testWithIntl.tsx
similarity index 53%
rename from frontend/src/utils/testWithIntl.js
rename to frontend/src/utils/testWithIntl.tsx
index 093c44a1db..78e79f6dfa 100644
--- a/frontend/src/utils/testWithIntl.js
+++ b/frontend/src/utils/testWithIntl.tsx
@@ -7,39 +7,51 @@ import { QueryClientProvider, QueryClient } from '@tanstack/react-query';
import { store } from '../store';
import userEvent from '@testing-library/user-event';
+import { Store } from 'redux';
-export const createComponentWithIntl = (children, props = { locale: 'en' }) => {
- return TestRenderer.create({children} );
+export const createComponentWithIntl = (props: {
+ locale?: string,
+ children: React.ReactNode,
+}) => {
+ return TestRenderer.create({props.children} );
};
-export const createComponentWithReduxAndIntl = (children, props = { locale: 'en' }) => {
- return TestRenderer.create({children} );
+export const createComponentWithReduxAndIntl = (props: {
+ locale?: string,
+ children: React.ReactNode,
+}) => {
+ return TestRenderer.create({props.children} );
};
-export const renderWithRouter = (ui, { route = '/' } = {}) => {
+export const renderWithRouter = (ui: React.ReactNode, { route = '/' } = {}) => {
act(() => window.history.pushState({}, 'Test page', route));
return {
- user: userEvent.setup({ copyToClipboard: true }),
+ user: userEvent.setup({ writeToClipboard: true }),
...render(ui, { wrapper: BrowserRouter }),
};
};
-export const ReduxIntlProviders = ({
- children,
- props = { locale: 'en' },
- localStore = null,
-}: Object) => (
-
- {children}
+export const ReduxIntlProviders = (props: {
+ children: React.ReactNode,
+ locale?: string,
+ localStore?: Store,
+}) => (
+
+ {props.children}
);
-export const IntlProviders = ({ children, props = { locale: 'en' } }: Object) => (
- {children}
+export const IntlProviders = (props: {
+ children: React.ReactNode,
+ locale?: string,
+}) => (
+ {props.children}
);
-export const QueryClientProviders = ({ children }) => {
+export const QueryClientProviders = (props: {
+ children: React.ReactNode
+}) => {
const queryClient = new QueryClient({
defaultOptions: {
queries: {
@@ -50,14 +62,14 @@ export const QueryClientProviders = ({ children }) => {
log: console.log,
warn: console.warn,
// ✅ no more errors on the console for tests
- error: process.env.NODE_ENV === 'test' ? () => {} : console.error,
+ error: import.meta.env.NODE_ENV === 'test' ? () => { } : console.error,
},
});
- return {children} ;
+ return {props.children} ;
};
export const createComponentWithMemoryRouter = (
- component,
+ component: React.ReactNode,
{ route = '/starting/path', entryRoute = route } = {},
) => {
const user = userEvent.setup();
diff --git a/frontend/src/utils/tests/snippets/AOI.js b/frontend/src/utils/tests/snippets/AOI.ts
similarity index 100%
rename from frontend/src/utils/tests/snippets/AOI.js
rename to frontend/src/utils/tests/snippets/AOI.ts
diff --git a/frontend/src/utils/tests/snippets/feature.js b/frontend/src/utils/tests/snippets/feature.ts
similarity index 100%
rename from frontend/src/utils/tests/snippets/feature.js
rename to frontend/src/utils/tests/snippets/feature.ts
diff --git a/frontend/src/utils/tests/snippets/parsedGeometry.js b/frontend/src/utils/tests/snippets/parsedGeometry.ts
similarity index 100%
rename from frontend/src/utils/tests/snippets/parsedGeometry.js
rename to frontend/src/utils/tests/snippets/parsedGeometry.ts
diff --git a/frontend/src/utils/tests/snippets/projectMetadata.js b/frontend/src/utils/tests/snippets/projectMetadata.ts
similarity index 100%
rename from frontend/src/utils/tests/snippets/projectMetadata.js
rename to frontend/src/utils/tests/snippets/projectMetadata.ts
diff --git a/frontend/src/utils/tests/snippets/taskGrid.js b/frontend/src/utils/tests/snippets/taskGrid.ts
similarity index 100%
rename from frontend/src/utils/tests/snippets/taskGrid.js
rename to frontend/src/utils/tests/snippets/taskGrid.ts
diff --git a/frontend/src/utils/tests/snippets/tasksGeometry.js b/frontend/src/utils/tests/snippets/tasksGeometry.ts
similarity index 100%
rename from frontend/src/utils/tests/snippets/tasksGeometry.js
rename to frontend/src/utils/tests/snippets/tasksGeometry.ts
diff --git a/frontend/src/views/authorized.js b/frontend/src/views/authorized.tsx
similarity index 100%
rename from frontend/src/views/authorized.js
rename to frontend/src/views/authorized.tsx
diff --git a/frontend/src/views/fallback.js b/frontend/src/views/fallback.tsx
similarity index 100%
rename from frontend/src/views/fallback.js
rename to frontend/src/views/fallback.tsx
diff --git a/frontend/src/views/home.js b/frontend/src/views/home.jsx
similarity index 100%
rename from frontend/src/views/home.js
rename to frontend/src/views/home.jsx
diff --git a/frontend/src/views/management.js b/frontend/src/views/management.jsx
similarity index 100%
rename from frontend/src/views/management.js
rename to frontend/src/views/management.jsx
diff --git a/frontend/src/views/messages.js b/frontend/src/views/messages.ts
similarity index 100%
rename from frontend/src/views/messages.js
rename to frontend/src/views/messages.ts
diff --git a/frontend/src/views/notFound.js b/frontend/src/views/notFound.jsx
similarity index 100%
rename from frontend/src/views/notFound.js
rename to frontend/src/views/notFound.jsx
diff --git a/frontend/src/views/notifications.js b/frontend/src/views/notifications.jsx
similarity index 100%
rename from frontend/src/views/notifications.js
rename to frontend/src/views/notifications.jsx
diff --git a/frontend/src/views/projectStats.js b/frontend/src/views/projectStats.js
index 819ebd740e..2a6041b257 100644
--- a/frontend/src/views/projectStats.js
+++ b/frontend/src/views/projectStats.js
@@ -28,7 +28,7 @@ export function ProjectStats() {
const { id } = useParams();
useSetTitleTag(`Project #${id} Stats`);
const { data: project, status: projectStatus } = useProjectSummaryQuery(id, {
- useErrorBoundary: true,
+ throwOnError: true,
});
const { data: tasks, status: tasksStatus } = useTasksQuery(id);
const tasksByStatus = useTasksByStatus(tasks);
diff --git a/frontend/src/views/root.js b/frontend/src/views/root.jsx
similarity index 100%
rename from frontend/src/views/root.js
rename to frontend/src/views/root.jsx
diff --git a/frontend/src/views/taskAction.js b/frontend/src/views/taskAction.js
index 61f9bad259..c4aa5f4e7b 100644
--- a/frontend/src/views/taskAction.js
+++ b/frontend/src/views/taskAction.js
@@ -108,9 +108,9 @@ export function TaskAction({ projectId, action }: Object) {
export function TaskActionPossible({ projectId, lockedTasks, action, editor, getTasks }) {
const { data: project, status: projectStatus } = useProjectSummaryQuery(projectId, {
- useErrorBoundary: true,
+ throwOnError: true,
});
- const { data: tasks, status: tasksStatus } = useTasksQuery(projectId, { useErrorBoundary: true });
+ const { data: tasks, status: tasksStatus } = useTasksQuery(projectId, { throwOnError: true });
return (
diff --git a/frontend/src/views/taskSelection.js b/frontend/src/views/taskSelection.js
index 2b8cfe7f9b..59b55e1e47 100644
--- a/frontend/src/views/taskSelection.js
+++ b/frontend/src/views/taskSelection.js
@@ -12,7 +12,7 @@ export function SelectTask() {
const navigate = useNavigate();
const token = useSelector((state) => state.auth.token);
const { data, status, error } = useProjectSummaryQuery(id, {
- useErrorBoundary: (error) => error.response.status !== 404,
+ throwOnError: (error) => error.response.status !== 404,
});
useEffect(() => {
diff --git a/frontend/src/views/tests/notFound.test.js b/frontend/src/views/tests/notFound.test.js
index 20131ca30c..05c643cc89 100644
--- a/frontend/src/views/tests/notFound.test.js
+++ b/frontend/src/views/tests/notFound.test.js
@@ -2,7 +2,7 @@ import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/react';
import { IntlProviders } from '../../utils/testWithIntl';
import messages from '../messages';
-import { NotFound } from '../notFound';
+import { NotFound } from '../notFound.jsx';
describe('Not Found', () => {
it('should display project not found error', () => {
diff --git a/frontend/src/views/welcome.js b/frontend/src/views/welcome.jsx
similarity index 100%
rename from frontend/src/views/welcome.js
rename to frontend/src/views/welcome.jsx
diff --git a/frontend/src/vite.d.ts b/frontend/src/vite.d.ts
new file mode 100644
index 0000000000..11f02fe2a0
--- /dev/null
+++ b/frontend/src/vite.d.ts
@@ -0,0 +1 @@
+///
diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json
new file mode 100644
index 0000000000..ff4041ee12
--- /dev/null
+++ b/frontend/tsconfig.json
@@ -0,0 +1,30 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "lib": [
+ "ES2020",
+ "DOM",
+ "DOM.Iterable"
+ ],
+ "module": "ESNext",
+ "skipLibCheck": true,
+ "allowJs": true,
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "isolatedModules": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
+ /* Linting */
+ "strict": false,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true,
+ "noImplicitAny": true
+ },
+ "include": [
+ "src"
+ ]
+}
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
new file mode 100644
index 0000000000..e9687eca3e
--- /dev/null
+++ b/frontend/vite.config.ts
@@ -0,0 +1,14 @@
+// vite.config.js
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react-swc'
+
+export default defineConfig({
+ plugins: [react()],
+ esbuild: {
+ loader: "jsx",
+ include: /src\/.*\.jsx?$/,
+ // loader: "tsx",
+ // include: /src\/.*\.[tj]sx?$/,
+ exclude: [],
+ },
+})
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
index 3837a4c19c..c49fefd6ad 100644
--- a/frontend/yarn.lock
+++ b/frontend/yarn.lock
@@ -1396,6 +1396,121 @@
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz#d0fce5d07b0620caa282b5131c297bb60f9d87e6"
integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==
+"@esbuild/aix-ppc64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f"
+ integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==
+
+"@esbuild/android-arm64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052"
+ integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==
+
+"@esbuild/android-arm@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28"
+ integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==
+
+"@esbuild/android-x64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e"
+ integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==
+
+"@esbuild/darwin-arm64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a"
+ integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==
+
+"@esbuild/darwin-x64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22"
+ integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==
+
+"@esbuild/freebsd-arm64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e"
+ integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==
+
+"@esbuild/freebsd-x64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261"
+ integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==
+
+"@esbuild/linux-arm64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b"
+ integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==
+
+"@esbuild/linux-arm@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9"
+ integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==
+
+"@esbuild/linux-ia32@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2"
+ integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==
+
+"@esbuild/linux-loong64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df"
+ integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==
+
+"@esbuild/linux-mips64el@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe"
+ integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==
+
+"@esbuild/linux-ppc64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4"
+ integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==
+
+"@esbuild/linux-riscv64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc"
+ integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==
+
+"@esbuild/linux-s390x@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de"
+ integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==
+
+"@esbuild/linux-x64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0"
+ integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==
+
+"@esbuild/netbsd-x64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047"
+ integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==
+
+"@esbuild/openbsd-x64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70"
+ integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==
+
+"@esbuild/sunos-x64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b"
+ integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==
+
+"@esbuild/win32-arm64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d"
+ integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==
+
+"@esbuild/win32-ia32@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b"
+ integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==
+
+"@esbuild/win32-x64@0.21.5":
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c"
+ integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==
+
"@eslint-community/eslint-utils@^4.2.0":
version "4.4.0"
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
@@ -2708,6 +2823,14 @@
resolved "https://registry.yarnpkg.com/@reach/auto-id/-/auto-id-0.2.0.tgz#97f9e48fe736aa5c6f4f32cf73c1f19d005f8550"
integrity sha512-lVK/svL2HuQdp7jgvlrLkFsUx50Az9chAhxpiPwBqcS83I2pVWvXp98FOcSCCJCV++l115QmzHhFd+ycw1zLBg==
+"@redux-devtools/extension@^3.3.0":
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/@redux-devtools/extension/-/extension-3.3.0.tgz#bc775d289f15604c472112920beac2cf4dbb7907"
+ integrity sha512-X34S/rC8S/M1BIrkYD1mJ5f8vlH0BDqxXrs96cvxSBo4FhMdbhU+GUGsmNYov1xjSyLMHgo8NYrUG8bNX7525g==
+ dependencies:
+ "@babel/runtime" "^7.23.2"
+ immutable "^4.3.4"
+
"@remix-run/router@1.16.0":
version "1.16.0"
resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.16.0.tgz#0e10181e5fec1434eb071a9bc4bdaac843f16dcc"
@@ -2750,6 +2873,86 @@
estree-walker "^1.0.1"
picomatch "^2.2.2"
+"@rollup/rollup-android-arm-eabi@4.21.0":
+ version "4.21.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.0.tgz#d941173f82f9b041c61b0dc1a2a91dcd06e4b31e"
+ integrity sha512-WTWD8PfoSAJ+qL87lE7votj3syLavxunWhzCnx3XFxFiI/BA/r3X7MUM8dVrH8rb2r4AiO8jJsr3ZjdaftmnfA==
+
+"@rollup/rollup-android-arm64@4.21.0":
+ version "4.21.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.0.tgz#7e7157c8543215245ceffc445134d9e843ba51c0"
+ integrity sha512-a1sR2zSK1B4eYkiZu17ZUZhmUQcKjk2/j9Me2IDjk1GHW7LB5Z35LEzj9iJch6gtUfsnvZs1ZNyDW2oZSThrkA==
+
+"@rollup/rollup-darwin-arm64@4.21.0":
+ version "4.21.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.0.tgz#f0a18a4fc8dc6eb1e94a51fa2adb22876f477947"
+ integrity sha512-zOnKWLgDld/svhKO5PD9ozmL6roy5OQ5T4ThvdYZLpiOhEGY+dp2NwUmxK0Ld91LrbjrvtNAE0ERBwjqhZTRAA==
+
+"@rollup/rollup-darwin-x64@4.21.0":
+ version "4.21.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.0.tgz#34b7867613e5cc42d2b85ddc0424228cc33b43f0"
+ integrity sha512-7doS8br0xAkg48SKE2QNtMSFPFUlRdw9+votl27MvT46vo44ATBmdZdGysOevNELmZlfd+NEa0UYOA8f01WSrg==
+
+"@rollup/rollup-linux-arm-gnueabihf@4.21.0":
+ version "4.21.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.0.tgz#422b19ff9ae02b05d3395183d1d43b38c7c8be0b"
+ integrity sha512-pWJsfQjNWNGsoCq53KjMtwdJDmh/6NubwQcz52aEwLEuvx08bzcy6tOUuawAOncPnxz/3siRtd8hiQ32G1y8VA==
+
+"@rollup/rollup-linux-arm-musleabihf@4.21.0":
+ version "4.21.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.0.tgz#568aa29195ef6fc57ec6ed3f518923764406a8ee"
+ integrity sha512-efRIANsz3UHZrnZXuEvxS9LoCOWMGD1rweciD6uJQIx2myN3a8Im1FafZBzh7zk1RJ6oKcR16dU3UPldaKd83w==
+
+"@rollup/rollup-linux-arm64-gnu@4.21.0":
+ version "4.21.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.0.tgz#22309c8bcba9a73114f69165c72bc94b2fbec085"
+ integrity sha512-ZrPhydkTVhyeGTW94WJ8pnl1uroqVHM3j3hjdquwAcWnmivjAwOYjTEAuEDeJvGX7xv3Z9GAvrBkEzCgHq9U1w==
+
+"@rollup/rollup-linux-arm64-musl@4.21.0":
+ version "4.21.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.0.tgz#c93c388af6d33f082894b8a60839d7265b2b9bc5"
+ integrity sha512-cfaupqd+UEFeURmqNP2eEvXqgbSox/LHOyN9/d2pSdV8xTrjdg3NgOFJCtc1vQ/jEke1qD0IejbBfxleBPHnPw==
+
+"@rollup/rollup-linux-powerpc64le-gnu@4.21.0":
+ version "4.21.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.0.tgz#493c5e19e395cf3c6bd860c7139c8a903dea72b4"
+ integrity sha512-ZKPan1/RvAhrUylwBXC9t7B2hXdpb/ufeu22pG2psV7RN8roOfGurEghw1ySmX/CmDDHNTDDjY3lo9hRlgtaHg==
+
+"@rollup/rollup-linux-riscv64-gnu@4.21.0":
+ version "4.21.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.0.tgz#a2eab4346fbe5909165ce99adb935ba30c9fb444"
+ integrity sha512-H1eRaCwd5E8eS8leiS+o/NqMdljkcb1d6r2h4fKSsCXQilLKArq6WS7XBLDu80Yz+nMqHVFDquwcVrQmGr28rg==
+
+"@rollup/rollup-linux-s390x-gnu@4.21.0":
+ version "4.21.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.0.tgz#0bc49a79db4345d78d757bb1b05e73a1b42fa5c3"
+ integrity sha512-zJ4hA+3b5tu8u7L58CCSI0A9N1vkfwPhWd/puGXwtZlsB5bTkwDNW/+JCU84+3QYmKpLi+XvHdmrlwUwDA6kqw==
+
+"@rollup/rollup-linux-x64-gnu@4.21.0":
+ version "4.21.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.0.tgz#4fd36a6a41f3406d8693321b13d4f9b7658dd4b9"
+ integrity sha512-e2hrvElFIh6kW/UNBQK/kzqMNY5mO+67YtEh9OA65RM5IJXYTWiXjX6fjIiPaqOkBthYF1EqgiZ6OXKcQsM0hg==
+
+"@rollup/rollup-linux-x64-musl@4.21.0":
+ version "4.21.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.0.tgz#10ebb13bd4469cbad1a5d9b073bd27ec8a886200"
+ integrity sha512-1vvmgDdUSebVGXWX2lIcgRebqfQSff0hMEkLJyakQ9JQUbLDkEaMsPTLOmyccyC6IJ/l3FZuJbmrBw/u0A0uCQ==
+
+"@rollup/rollup-win32-arm64-msvc@4.21.0":
+ version "4.21.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.0.tgz#2fef1a90f1402258ef915ae5a94cc91a5a1d5bfc"
+ integrity sha512-s5oFkZ/hFcrlAyBTONFY1TWndfyre1wOMwU+6KCpm/iatybvrRgmZVM+vCFwxmC5ZhdlgfE0N4XorsDpi7/4XQ==
+
+"@rollup/rollup-win32-ia32-msvc@4.21.0":
+ version "4.21.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.0.tgz#a18ad47a95c5f264defb60acdd8c27569f816fc1"
+ integrity sha512-G9+TEqRnAA6nbpqyUqgTiopmnfgnMkR3kMukFBDsiyy23LZvUCpiUwjTRx6ezYCjJODXrh52rBR9oXvm+Fp5wg==
+
+"@rollup/rollup-win32-x64-msvc@4.21.0":
+ version "4.21.0"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.0.tgz#20c09cf44dcb082140cc7f439dd679fe4bba3375"
+ integrity sha512-2jsCDZwtQvRhejHLfZ1JY6w6kEuEtfF9nzYsZxzSlNVKDX+DpsDJ+Rbjkm74nvg2rdx0gwBS+IMdvwJuq3S9pQ==
+
"@rushstack/eslint-patch@^1.1.0":
version "1.10.2"
resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.10.2.tgz#053f1540703faa81dea2966b768ee5581c66aeda"
@@ -3044,6 +3247,87 @@
"@svgr/plugin-svgo" "^5.5.0"
loader-utils "^2.0.0"
+"@swc/core-darwin-arm64@1.7.14":
+ version "1.7.14"
+ resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.14.tgz#a4530ec755ea183802cc9dfe4900ab5f6a327fea"
+ integrity sha512-V0OUXjOH+hdGxDYG8NkQzy25mKOpcNKFpqtZEzLe5V/CpLJPnpg1+pMz70m14s9ZFda9OxsjlvPbg1FLUwhgIQ==
+
+"@swc/core-darwin-x64@1.7.14":
+ version "1.7.14"
+ resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.7.14.tgz#2c9c717fd28dd1dde9c21cf58b01f1cda7976b1a"
+ integrity sha512-9iFvUnxG6FC3An5ogp5jbBfQuUmTTwy8KMB+ZddUoPB3NR1eV+Y9vOh/tfWcenSJbgOKDLgYC5D/b1mHAprsrQ==
+
+"@swc/core-linux-arm-gnueabihf@1.7.14":
+ version "1.7.14"
+ resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.14.tgz#fed055c9c65347177c8df88720f8a51793a4df06"
+ integrity sha512-zGJsef9qPivKSH8Vv4F/HiBXBTHZ5Hs3ZjVGo/UIdWPJF8fTL9OVADiRrl34Q7zOZEtGXRwEKLUW1SCQcbDvZA==
+
+"@swc/core-linux-arm64-gnu@1.7.14":
+ version "1.7.14"
+ resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.14.tgz#ca740c8ea26f041b2dc43ba87facec452052814f"
+ integrity sha512-AxV3MPsoI7i4B8FXOew3dx3N8y00YoJYvIPfxelw07RegeCEH3aHp2U2DtgbP/NV1ugZMx0TL2Z2DEvocmA51g==
+
+"@swc/core-linux-arm64-musl@1.7.14":
+ version "1.7.14"
+ resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.14.tgz#fbc6fed24f5ad58b948e5b7abe6cd1f07112bef1"
+ integrity sha512-JDLdNjUj3zPehd4+DrQD8Ltb3B5lD8D05IwePyDWw+uR/YPc7w/TX1FUVci5h3giJnlMCJRvi1IQYV7K1n7KtQ==
+
+"@swc/core-linux-x64-gnu@1.7.14":
+ version "1.7.14"
+ resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.14.tgz#509a37833e4fbf89506b9291d9bd131fa2017fca"
+ integrity sha512-Siy5OvPCLLWmMdx4msnEs8HvEVUEigSn0+3pbLjv78iwzXd0qSBNHUPZyC1xeurVaUbpNDxZTpPRIwpqNE2+Og==
+
+"@swc/core-linux-x64-musl@1.7.14":
+ version "1.7.14"
+ resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.14.tgz#81156cc6ff814ad4b8fcf6eb6658d3f247db0b57"
+ integrity sha512-FtEGm9mwtRYQNK43WMtUIadxHs/ja2rnDurB99os0ZoFTGG2IHuht2zD97W0wB8JbqEabT1XwSG9Y5wmN+ciEQ==
+
+"@swc/core-win32-arm64-msvc@1.7.14":
+ version "1.7.14"
+ resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.14.tgz#c605fa783b5fbe1fff784ace4c4bb074b8d6026d"
+ integrity sha512-Jp8KDlfq7Ntt2/BXr0y344cYgB1zf0DaLzDZ1ZJR6rYlAzWYSccLYcxHa97VGnsYhhPspMpmCvHid97oe2hl4A==
+
+"@swc/core-win32-ia32-msvc@1.7.14":
+ version "1.7.14"
+ resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.14.tgz#3e15dc3b662c9fab851a38b3e271c8e2da4ba03a"
+ integrity sha512-I+cFsXF0OU0J9J4zdWiQKKLURO5dvCujH9Jr8N0cErdy54l9d4gfIxdctfTF+7FyXtWKLTCkp+oby9BQhkFGWA==
+
+"@swc/core-win32-x64-msvc@1.7.14":
+ version "1.7.14"
+ resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.14.tgz#83958d92e9f07865ec9365212111fbc295660f0d"
+ integrity sha512-NNrprQCK6d28mG436jVo2TD+vACHseUECacEBGZ9Ef0qfOIWS1XIt2MisQKG0Oea2VvLFl6tF/V4Lnx/H0Sn3Q==
+
+"@swc/core@^1.5.7":
+ version "1.7.14"
+ resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.7.14.tgz#d10492b5a4168cb1e73cf561a315e8b0f62255ed"
+ integrity sha512-9aeXeifnyuvc2pcuuhPQgVUwdpGEzZ+9nJu0W8/hNl/aESFsJGR5i9uQJRGu0atoNr01gK092fvmqMmQAPcKow==
+ dependencies:
+ "@swc/counter" "^0.1.3"
+ "@swc/types" "^0.1.12"
+ optionalDependencies:
+ "@swc/core-darwin-arm64" "1.7.14"
+ "@swc/core-darwin-x64" "1.7.14"
+ "@swc/core-linux-arm-gnueabihf" "1.7.14"
+ "@swc/core-linux-arm64-gnu" "1.7.14"
+ "@swc/core-linux-arm64-musl" "1.7.14"
+ "@swc/core-linux-x64-gnu" "1.7.14"
+ "@swc/core-linux-x64-musl" "1.7.14"
+ "@swc/core-win32-arm64-msvc" "1.7.14"
+ "@swc/core-win32-ia32-msvc" "1.7.14"
+ "@swc/core-win32-x64-msvc" "1.7.14"
+
+"@swc/counter@^0.1.3":
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/@swc/counter/-/counter-0.1.3.tgz#cc7463bd02949611c6329596fccd2b0ec782b0e9"
+ integrity sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==
+
+"@swc/types@^0.1.12":
+ version "0.1.12"
+ resolved "https://registry.yarnpkg.com/@swc/types/-/types-0.1.12.tgz#7f632c06ab4092ce0ebd046ed77ff7557442282f"
+ integrity sha512-wBJA+SdtkbFhHjTMYH+dEH1y4VpfGdAc2Kw/LK09i9bXd/K6j6PkDcFCEzb6iVfZMkPRrl/q0e3toqTAJdkIVA==
+ dependencies:
+ "@swc/counter" "^0.1.3"
+
"@szmarczak/http-timer@^4.0.5":
version "4.0.6"
resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807"
@@ -3056,34 +3340,29 @@
resolved "https://registry.yarnpkg.com/@tanstack/eslint-plugin-query/-/eslint-plugin-query-4.38.0.tgz#a8ffd5b4187ed0b522329a1a950fbc3d467e5167"
integrity sha512-KmcrnjTQzONBqxNWSVKyPNi5tLq0URvIiWThE9HIK5qePGtB0VqoHfOsn4nuGJD268xDNDpFQjQiko9mMa5iLQ==
-"@tanstack/match-sorter-utils@^8.7.0":
- version "8.15.1"
- resolved "https://registry.yarnpkg.com/@tanstack/match-sorter-utils/-/match-sorter-utils-8.15.1.tgz#715e028ff43cf79ece10bd5a757047a1016c3bba"
- integrity sha512-PnVV3d2poenUM31ZbZi/yXkBu3J7kd5k2u51CGwwNojag451AjTH9N6n41yjXz2fpLeewleyLBmNS6+HcGDlXw==
- dependencies:
- remove-accents "0.5.0"
+"@tanstack/query-core@5.52.0":
+ version "5.52.0"
+ resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.52.0.tgz#44070b2d6eb58c3a5ce2788471d842e932294a87"
+ integrity sha512-U1DOEgltjUwalN6uWYTewSnA14b+tE7lSylOiASKCAO61ENJeCq9VVD/TXHA6O5u9+6v5+UgGYBSccTKDoyMqw==
-"@tanstack/query-core@4.36.1":
- version "4.36.1"
- resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-4.36.1.tgz#79f8c1a539d47c83104210be2388813a7af2e524"
- integrity sha512-DJSilV5+ytBP1FbFcEJovv4rnnm/CokuVvrBEtW/Va9DvuJ3HksbXUJEpI0aV1KtuL4ZoO9AVE6PyNLzF7tLeA==
+"@tanstack/query-devtools@5.51.16":
+ version "5.51.16"
+ resolved "https://registry.yarnpkg.com/@tanstack/query-devtools/-/query-devtools-5.51.16.tgz#d855d00e7939c1a442c2e8ae3ad1a5bd603d003b"
+ integrity sha512-ajwuq4WnkNCMj/Hy3KR8d3RtZ6PSKc1dD2vs2T408MdjgKzQ3klVoL6zDgVO7X+5jlb5zfgcO3thh4ojPhfIaw==
-"@tanstack/react-query-devtools@^4.29.7":
- version "4.36.1"
- resolved "https://registry.yarnpkg.com/@tanstack/react-query-devtools/-/react-query-devtools-4.36.1.tgz#7e63601135902a993ca9af73507b125233b1554e"
- integrity sha512-WYku83CKP3OevnYSG8Y/QO9g0rT75v1om5IvcWUwiUZJ4LanYGLVCZ8TdFG5jfsq4Ej/lu2wwDAULEUnRIMBSw==
+"@tanstack/react-query-devtools@^5.52.0":
+ version "5.52.1"
+ resolved "https://registry.yarnpkg.com/@tanstack/react-query-devtools/-/react-query-devtools-5.52.1.tgz#5c15eef755a76ce45099206e0448cd120b3af9dc"
+ integrity sha512-6KwSm9vcIIK26osQJtT52Gyccz/DyHPT216B7kt4ihg22G2rtGZ7sCXYQO25XEHyVwVingwHdZ7auj+ydgg4Zg==
dependencies:
- "@tanstack/match-sorter-utils" "^8.7.0"
- superjson "^1.10.0"
- use-sync-external-store "^1.2.0"
+ "@tanstack/query-devtools" "5.51.16"
-"@tanstack/react-query@^4.29.7":
- version "4.36.1"
- resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-4.36.1.tgz#acb589fab4085060e2e78013164868c9c785e5d2"
- integrity sha512-y7ySVHFyyQblPl3J3eQBWpXZkliroki3ARnBKsdJchlgt7yJLRDUcf4B8soufgiYt3pEQIkBWBx1N9/ZPIeUWw==
+"@tanstack/react-query@^5.52.0":
+ version "5.52.0"
+ resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.52.0.tgz#671478798f1873983807cf6f62b140c817b3cc9f"
+ integrity sha512-T8tLZdPEopSD3A1EBZ/sq7WkI76pKLKKiT82F486K8wf26EPgYCdeiSnJfuayssdQjWwLQMQVl/ROUBNmlWgCQ==
dependencies:
- "@tanstack/query-core" "4.36.1"
- use-sync-external-store "^1.2.0"
+ "@tanstack/query-core" "5.52.0"
"@testing-library/dom@>=7":
version "10.1.0"
@@ -3390,6 +3669,13 @@
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.16.tgz#b1572967f0b8b60bf3f87fe1d854a5604ea70c82"
integrity sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==
+"@types/chart.js@^2.9.41":
+ version "2.9.41"
+ resolved "https://registry.yarnpkg.com/@types/chart.js/-/chart.js-2.9.41.tgz#4148cdc87d4f98fad44b2883271cd0fa57f05e0d"
+ integrity sha512-3dvkDvueckY83UyUXtJMalYoH6faOLkWQoaTlJgB4Djde3oORmNP0Jw85HtzTuXyliUHcdp704s0mZFQKio/KQ==
+ dependencies:
+ moment "^2.10.2"
+
"@types/connect-history-api-fallback@^1.3.5":
version "1.5.4"
resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz#7de71645a103056b48ac3ce07b3520b819c1d5b3"
@@ -3427,6 +3713,13 @@
dependencies:
"@types/ms" "*"
+"@types/dompurify@^3.0.5":
+ version "3.0.5"
+ resolved "https://registry.yarnpkg.com/@types/dompurify/-/dompurify-3.0.5.tgz#02069a2fcb89a163bacf1a788f73cb415dd75cb7"
+ integrity sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==
+ dependencies:
+ "@types/trusted-types" "*"
+
"@types/earcut@^2.1.0":
version "2.1.4"
resolved "https://registry.yarnpkg.com/@types/earcut/-/earcut-2.1.4.tgz#5811d7d333048f5a7573b22ddc84923e69596da6"
@@ -3448,7 +3741,7 @@
"@types/estree" "*"
"@types/json-schema" "*"
-"@types/estree@*", "@types/estree@^1.0.5":
+"@types/estree@*", "@types/estree@1.0.5", "@types/estree@^1.0.5":
version "1.0.5"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4"
integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==
@@ -3700,6 +3993,13 @@
dependencies:
"@types/react" "*"
+"@types/react-test-renderer@^18.3.0":
+ version "18.3.0"
+ resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-18.3.0.tgz#839502eae70058a4ae161f63385a8e7929cef4c0"
+ integrity sha512-HW4MuEYxfDbOHQsVlY/XtOvNHftCVEPhJF2pQXXwcUiUF+Oyb0usgp48HSgpK5rt8m9KZb22yqOeZm+rrVG8gw==
+ dependencies:
+ "@types/react" "*"
+
"@types/react-transition-group@^4.4.0":
version "4.4.10"
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.10.tgz#6ee71127bdab1f18f11ad8fb3322c6da27c327ac"
@@ -3775,6 +4075,11 @@
dependencies:
"@types/node" "*"
+"@types/slug@^5.0.9":
+ version "5.0.9"
+ resolved "https://registry.yarnpkg.com/@types/slug/-/slug-5.0.9.tgz#e5b213a9d7797d40d362ba85e2a7bbcd4df4ed40"
+ integrity sha512-6Yp8BSplP35Esa/wOG1wLNKiqXevpQTEF/RcL/NV6BBQaMmZh4YlDwCgrrFSoUE4xAGvnKd5c+lkQJmPrBAzfQ==
+
"@types/sockjs@^0.3.33":
version "0.3.36"
resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.36.tgz#ce322cf07bcc119d4cbf7f88954f3a3bd0f67535"
@@ -3801,7 +4106,7 @@
dependencies:
"@types/jest" "*"
-"@types/trusted-types@^2.0.2":
+"@types/trusted-types@*", "@types/trusted-types@^2.0.2":
version "2.0.7"
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11"
integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==
@@ -3976,6 +4281,13 @@
resolved "https://registry.yarnpkg.com/@vannizhang/wayback-core/-/wayback-core-1.0.6.tgz#43f6a6d3fc4a017f3483cb07c0f49498c14ef884"
integrity sha512-Ee5oaqy2vSUyxid+ZoYY0ZEUrNVWxi0wXwp8eU4jpelEneIt9HWSOOtT8Y5RVEAWgMhQQfWsqJZhh7KZxgduVg==
+"@vitejs/plugin-react-swc@^3.7.0":
+ version "3.7.0"
+ resolved "https://registry.yarnpkg.com/@vitejs/plugin-react-swc/-/plugin-react-swc-3.7.0.tgz#e456c0a6d7f562268e1d231af9ac46b86ef47d88"
+ integrity sha512-yrknSb3Dci6svCd/qhHqhFPDSw0QtjumcqdKMoNNzmOl5lMXTTiqzjWtG4Qask2HdvvzaNgSunbQGet8/GrKdA==
+ dependencies:
+ "@swc/core" "^1.5.7"
+
"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1":
version "1.12.1"
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.12.1.tgz#bb16a0e8b1914f979f45864c23819cc3e3f0d4bb"
@@ -5411,13 +5723,6 @@ cookie@^0.4.2:
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432"
integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==
-copy-anything@^3.0.2:
- version "3.0.5"
- resolved "https://registry.yarnpkg.com/copy-anything/-/copy-anything-3.0.5.tgz#2d92dce8c498f790fa7ad16b01a1ae5a45b020a0"
- integrity sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==
- dependencies:
- is-what "^4.1.8"
-
core-js-bundle@^3.19.0:
version "3.37.0"
resolved "https://registry.yarnpkg.com/core-js-bundle/-/core-js-bundle-3.37.0.tgz#7d79cd80e4d3f5dd4e5a7f869b2575d9f84c0f2c"
@@ -6006,6 +6311,11 @@ date-fns@^2.30.0:
dependencies:
"@babel/runtime" "^7.21.0"
+date-fns@^3.6.0:
+ version "3.6.0"
+ resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-3.6.0.tgz#f20ca4fe94f8b754951b24240676e8618c0206bf"
+ integrity sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==
+
debug@2.6.9, debug@^2.6.0:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
@@ -6642,6 +6952,35 @@ es-to-primitive@^1.2.1:
is-date-object "^1.0.1"
is-symbol "^1.0.2"
+esbuild@^0.21.3:
+ version "0.21.5"
+ resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d"
+ integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==
+ optionalDependencies:
+ "@esbuild/aix-ppc64" "0.21.5"
+ "@esbuild/android-arm" "0.21.5"
+ "@esbuild/android-arm64" "0.21.5"
+ "@esbuild/android-x64" "0.21.5"
+ "@esbuild/darwin-arm64" "0.21.5"
+ "@esbuild/darwin-x64" "0.21.5"
+ "@esbuild/freebsd-arm64" "0.21.5"
+ "@esbuild/freebsd-x64" "0.21.5"
+ "@esbuild/linux-arm" "0.21.5"
+ "@esbuild/linux-arm64" "0.21.5"
+ "@esbuild/linux-ia32" "0.21.5"
+ "@esbuild/linux-loong64" "0.21.5"
+ "@esbuild/linux-mips64el" "0.21.5"
+ "@esbuild/linux-ppc64" "0.21.5"
+ "@esbuild/linux-riscv64" "0.21.5"
+ "@esbuild/linux-s390x" "0.21.5"
+ "@esbuild/linux-x64" "0.21.5"
+ "@esbuild/netbsd-x64" "0.21.5"
+ "@esbuild/openbsd-x64" "0.21.5"
+ "@esbuild/sunos-x64" "0.21.5"
+ "@esbuild/win32-arm64" "0.21.5"
+ "@esbuild/win32-ia32" "0.21.5"
+ "@esbuild/win32-x64" "0.21.5"
+
escalade@^3.1.1:
version "3.1.2"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27"
@@ -7384,7 +7723,7 @@ fs.realpath@^1.0.0:
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
-fsevents@^2.3.2, fsevents@~2.3.2:
+fsevents@^2.3.2, fsevents@~2.3.2, fsevents@~2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
@@ -8125,6 +8464,11 @@ immutable@^4.0.0:
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.5.tgz#f8b436e66d59f99760dc577f5c99a4fd2a5cc5a0"
integrity sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==
+immutable@^4.3.4:
+ version "4.3.7"
+ resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.7.tgz#c70145fc90d89fb02021e65c84eb0226e4e5a381"
+ integrity sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==
+
import-fresh@^3.1.0, import-fresh@^3.2.1:
version "3.3.0"
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
@@ -8575,11 +8919,6 @@ is-weakset@^2.0.3:
call-bind "^1.0.7"
get-intrinsic "^1.2.4"
-is-what@^4.1.8:
- version "4.1.16"
- resolved "https://registry.yarnpkg.com/is-what/-/is-what-4.1.16.tgz#1ad860a19da8b4895ad5495da3182ce2acdd7a6f"
- integrity sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==
-
is-wsl@^2.1.1, is-wsl@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271"
@@ -9789,10 +10128,10 @@ markdown-table@^3.0.0:
resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-3.0.3.tgz#e6331d30e493127e031dd385488b5bd326e4a6bd"
integrity sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==
-marked@^4.3.0:
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3"
- integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==
+marked@^14.0.0:
+ version "14.0.0"
+ resolved "https://registry.yarnpkg.com/marked/-/marked-14.0.0.tgz#79a1477358a59e0660276f8fec76de2c33f35d83"
+ integrity sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==
marked@~12.0.2:
version "12.0.2"
@@ -10400,6 +10739,11 @@ modern-normalize@^1.1.0:
resolved "https://registry.yarnpkg.com/modern-normalize/-/modern-normalize-1.1.0.tgz#da8e80140d9221426bd4f725c6e11283d34f90b7"
integrity sha512-2lMlY1Yc1+CUy0gw4H95uNN7vjbpoED7NNRSBHE25nWfLBdmMzFCsPshlzbxHz+gYMcBEUN8V4pU16prcdPSgA==
+moment@^2.10.2:
+ version "2.30.1"
+ resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae"
+ integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==
+
moo-color@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/moo-color/-/moo-color-1.0.3.tgz#d56435f8359c8284d83ac58016df7427febece74"
@@ -11061,6 +11405,11 @@ picocolors@^1.0.0:
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
+picocolors@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1"
+ integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==
+
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3, picomatch@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
@@ -11796,6 +12145,15 @@ postcss@^8.3.5, postcss@^8.4.23, postcss@^8.4.33, postcss@^8.4.4:
picocolors "^1.0.0"
source-map-js "^1.2.0"
+postcss@^8.4.41:
+ version "8.4.41"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.41.tgz#d6104d3ba272d882fe18fc07d15dc2da62fa2681"
+ integrity sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==
+ dependencies:
+ nanoid "^3.3.7"
+ picocolors "^1.0.1"
+ source-map-js "^1.2.0"
+
potpack@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/potpack/-/potpack-1.0.2.tgz#23b99e64eb74f5741ffe7656b5b5c4ddce8dfc14"
@@ -12767,11 +13125,6 @@ remark-rehype@^10.0.0:
mdast-util-to-hast "^12.1.0"
unified "^10.0.0"
-remove-accents@0.5.0:
- version "0.5.0"
- resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.5.0.tgz#77991f37ba212afba162e375b627631315bed687"
- integrity sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==
-
renderkid@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-3.0.0.tgz#5fd823e4d6951d37358ecc9a58b1f06836b6268a"
@@ -12932,6 +13285,31 @@ rollup@^2.43.1:
optionalDependencies:
fsevents "~2.3.2"
+rollup@^4.20.0:
+ version "4.21.0"
+ resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.21.0.tgz#28db5f5c556a5180361d35009979ccc749560b9d"
+ integrity sha512-vo+S/lfA2lMS7rZ2Qoubi6I5hwZwzXeUIctILZLbHI+laNtvhhOIon2S1JksA5UEDQ7l3vberd0fxK44lTYjbQ==
+ dependencies:
+ "@types/estree" "1.0.5"
+ optionalDependencies:
+ "@rollup/rollup-android-arm-eabi" "4.21.0"
+ "@rollup/rollup-android-arm64" "4.21.0"
+ "@rollup/rollup-darwin-arm64" "4.21.0"
+ "@rollup/rollup-darwin-x64" "4.21.0"
+ "@rollup/rollup-linux-arm-gnueabihf" "4.21.0"
+ "@rollup/rollup-linux-arm-musleabihf" "4.21.0"
+ "@rollup/rollup-linux-arm64-gnu" "4.21.0"
+ "@rollup/rollup-linux-arm64-musl" "4.21.0"
+ "@rollup/rollup-linux-powerpc64le-gnu" "4.21.0"
+ "@rollup/rollup-linux-riscv64-gnu" "4.21.0"
+ "@rollup/rollup-linux-s390x-gnu" "4.21.0"
+ "@rollup/rollup-linux-x64-gnu" "4.21.0"
+ "@rollup/rollup-linux-x64-musl" "4.21.0"
+ "@rollup/rollup-win32-arm64-msvc" "4.21.0"
+ "@rollup/rollup-win32-ia32-msvc" "4.21.0"
+ "@rollup/rollup-win32-x64-msvc" "4.21.0"
+ fsevents "~2.3.2"
+
run-async@^2.4.0:
version "2.4.1"
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"
@@ -13588,16 +13966,7 @@ string-natural-compare@^3.0.1:
resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==
-"string-width-cjs@npm:string-width@^4.2.0":
- version "4.2.3"
- resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
- integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
- dependencies:
- emoji-regex "^8.0.0"
- is-fullwidth-code-point "^3.0.0"
- strip-ansi "^6.0.1"
-
-string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
+"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -13697,14 +14066,7 @@ stringify-object@^3.3.0:
is-obj "^1.0.1"
is-regexp "^1.0.0"
-"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
- integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
- dependencies:
- ansi-regex "^5.0.1"
-
-strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -13815,13 +14177,6 @@ supercluster@^8.0.1:
dependencies:
kdbush "^4.0.2"
-superjson@^1.10.0:
- version "1.13.3"
- resolved "https://registry.yarnpkg.com/superjson/-/superjson-1.13.3.tgz#3bd64046f6c0a47062850bb3180ef352a471f930"
- integrity sha512-mJiVjfd2vokfDxsQPOwJ/PtanO87LhpYY88ubI5dUB1Ab58Txbyje3+jpm+/83R/fevaq/107NNhtYBLuoTrFg==
- dependencies:
- copy-anything "^3.0.2"
-
supports-color@^5.3.0, supports-color@^5.4.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
@@ -14603,7 +14958,7 @@ use-query-params@^2.2.1:
dependencies:
serialize-query-params "^2.0.2"
-use-sync-external-store@^1.0.0, use-sync-external-store@^1.2.0:
+use-sync-external-store@^1.0.0:
version "1.2.2"
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9"
integrity sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==
@@ -14712,6 +15067,17 @@ vfile@^5.0.0:
unist-util-stringify-position "^3.0.0"
vfile-message "^3.0.0"
+vite@^5.4.2:
+ version "5.4.2"
+ resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.2.tgz#8acb6ec4bfab823cdfc1cb2d6c53ed311bc4e47e"
+ integrity sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==
+ dependencies:
+ esbuild "^0.21.3"
+ postcss "^8.4.41"
+ rollup "^4.20.0"
+ optionalDependencies:
+ fsevents "~2.3.3"
+
vt-pbf@^3.1.1, vt-pbf@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/vt-pbf/-/vt-pbf-3.1.3.tgz#68fd150756465e2edae1cc5c048e063916dcfaac"
@@ -15316,7 +15682,7 @@ workbox-window@6.6.1:
"@types/trusted-types" "^2.0.2"
workbox-core "6.6.1"
-"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
+"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@@ -15334,15 +15700,6 @@ wrap-ansi@^6.0.1:
string-width "^4.1.0"
strip-ansi "^6.0.0"
-wrap-ansi@^7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
- integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
- dependencies:
- ansi-styles "^4.0.0"
- string-width "^4.1.0"
- strip-ansi "^6.0.0"
-
wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"