Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(frontend): migrate to TypeScript and Vite #6534

Draft
wants to merge 23 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
db6527a
chore(frontend/redux): begin migration to TypeScript
JoltCode Aug 20, 2024
4a8ba73
chore(frontend): begin migration to vite
JoltCode Aug 20, 2024
5e9572a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 20, 2024
34f63c6
chore(frontend/redux): migrate rest of reducers to TypeScript
JoltCode Aug 20, 2024
e1287d5
chore(frontend/utils): migrate safe_storage to TypeScript
JoltCode Aug 20, 2024
76bc48f
chore(frontend): loads more migration of files to ts
JoltCode Aug 20, 2024
5053181
Merge branch 'hotosm:develop' into typescript-vite
JoltCode Aug 20, 2024
a9f6636
fix,chore(frontend/redux): fix reducers combined return type
JoltCode Aug 20, 2024
0a5a7cd
chore(frontend): migrate footer to TS
JoltCode Aug 20, 2024
454d43c
chore(frontend): more ts migrations
JoltCode Aug 21, 2024
67a4f7b
chore(frontned): more TS migrations :D
JoltCode Aug 21, 2024
463f7b5
chore(frontend): more TS Migrations!
JoltCode Aug 21, 2024
e64e9ab
chore(frontend): hooks and utils migrations to TS
JoltCode Aug 21, 2024
e1d2b2b
chore(frontend): mostly comments and contributions TS migrations
JoltCode Aug 21, 2024
f4cd684
chore(frontend): migrate components and utils to TS
JoltCode Aug 22, 2024
bf8cfec
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 22, 2024
462993e
chore(frontend): more migrations to TS
JoltCode Aug 22, 2024
0060e30
chore(frontend): TS Migrations, allow JS in tsconfig
JoltCode Aug 23, 2024
8fe300f
chore(frontend): migrate loads of svg icons to TS, fix vite config
JoltCode Aug 23, 2024
1d9811e
chore(frontend): migrate more svg icons to TS
JoltCode Aug 23, 2024
1f1a417
chore(frontend): migrate more svgIcons to TS
JoltCode Aug 24, 2024
20f7aaf
chore(frontend/svgIcons): migrate final svgIcons to TS and functional…
JoltCode Aug 24, 2024
dad2a63
chore(frontend): lots of TS Migrations - base site now rendering - al…
JoltCode Aug 24, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Status | Feature





## Developers

Expand Down
10 changes: 7 additions & 3 deletions frontend/public/index.html β†’ frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html>
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/static/favicon.ico" />
<link rel="shortcut icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

<!-- for Google -->
Expand All @@ -27,7 +27,7 @@
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="manifest" href="/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Expand All @@ -36,12 +36,15 @@
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.

%PUBLIC_URL% is now no longer used, it is automatically refactored to the correct path during the build process.
-->
<title>%REACT_APP_ORG_CODE% Tasking Manager</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script type="module" src="/src/index.tsx"></script>
<div
id="action-root"
class="w-100 h-100 vh-minus-122-ns"
Expand All @@ -59,7 +62,8 @@
var site_id = '%REACT_APP_MATOMO_ID%';
if (site_id && '%REACT_APP_MATOMO_ENDPOINT%') {
(function () {
var u = '%REACT_APP_MATOMO_ENDPOINT%';
// var u = '%REACT_APP_MATOMO_ENDPOINT%';
var u = 'https://matomo.hotosm.org/'
_paq.push(['setTrackerUrl', u + 'piwik.php']);
_paq.push(['setSiteId', site_id]);

Expand Down
20 changes: 14 additions & 6 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
"@mapbox/mapbox-gl-language": "^0.10.1",
"@placemarkio/geo-viewport": "^1.0.2",
"@rapideditor/rapid": "^2.3.1",
"@redux-devtools/extension": "^3.3.0",
"@sentry/react": "^7.102.0",
"@tanstack/react-query": "^4.29.7",
"@tanstack/react-query-devtools": "^4.29.7",
"@tanstack/react-query": "^5.52.0",
"@tanstack/react-query-devtools": "^5.52.0",
"@tmcw/togeojson": "^5.8.1",
"@turf/area": "^6.5.0",
"@turf/bbox": "^6.5.0",
Expand All @@ -29,15 +30,15 @@
"axios": "^1.6.7",
"chart.js": "^4.4.1",
"chartjs-adapter-date-fns": "^3.0.0",
"date-fns": "^2.30.0",
"date-fns": "^3.6.0",
"dompurify": "^3.0.9",
"downshift-hooks": "^0.8.1",
"final-form": "^4.20.10",
"fromentries": "^1.3.2",
"humanize-duration": "^3.31.0",
"mapbox-gl": "^1.13.3",
"mapbox-gl-draw-rectangle-mode": "^1.0.4",
"marked": "^4.3.0",
"marked": "^14.0.0",
"osmtogeojson": "^3.0.0-beta.5",
"prop-types": "^15.8.1",
"query-string": "^8.2.0",
Expand Down Expand Up @@ -85,7 +86,8 @@
"patch-id": "bash -c \"cp patch/imagery.min.json public/static/id/data\"",
"patch-rapid": "bash -c \"cp patch/rapid-imagery.min.json public/static/rapid/data/imagery.min.json\"",
"preparation": "bash -c \"if (test -a ../tasking-manager.env); then grep -hs ^ ../tasking-manager.env .env.expand > .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",
Expand Down Expand Up @@ -117,14 +119,20 @@
"@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",
"prettier": "^2.8.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"
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/App.js β†’ frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
});
Expand All @@ -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) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
85 changes: 51 additions & 34 deletions frontend/src/api/projects.js β†’ frontend/src/api/projects.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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,
Expand All @@ -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,
});
Expand All @@ -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,
});
Expand All @@ -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,
});
Expand All @@ -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,
});
Expand All @@ -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,
});
Expand All @@ -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,
});
Expand All @@ -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,
});
Expand All @@ -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,
});
Expand All @@ -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,
Expand All @@ -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/');
};
Expand Down
Loading
Loading