From a358e9d996efde59415863274471b3c896020288 Mon Sep 17 00:00:00 2001 From: crssstha Date: Wed, 20 May 2026 16:39:03 +0545 Subject: [PATCH 1/4] feat(document): added manual, policies, guidelines and online interactive pages --- app/Root/config/routes.ts | 40 +++++ app/components/DocumentCard/index.tsx | 46 +++++ app/components/DocumentCard/styles.module.css | 6 + app/components/Footer/index.tsx | 31 ++-- app/views/AdditionalLinks/index.tsx | 165 ++++++++++++++++++ app/views/Guidelines/index.tsx | 61 +++++++ app/views/Manuals/index.tsx | 61 +++++++ app/views/OnlineInteractive/index.tsx | 72 ++++++++ app/views/Polices/index.tsx | 61 +++++++ 9 files changed, 530 insertions(+), 13 deletions(-) create mode 100644 app/components/DocumentCard/index.tsx create mode 100644 app/components/DocumentCard/styles.module.css create mode 100644 app/views/AdditionalLinks/index.tsx create mode 100644 app/views/Guidelines/index.tsx create mode 100644 app/views/Manuals/index.tsx create mode 100644 app/views/OnlineInteractive/index.tsx create mode 100644 app/views/Polices/index.tsx diff --git a/app/Root/config/routes.ts b/app/Root/config/routes.ts index db2cb53..f823f67 100644 --- a/app/Root/config/routes.ts +++ b/app/Root/config/routes.ts @@ -130,6 +130,41 @@ const login: RouteConfig = { visibility: 'is-not-authenticated', }; +const additionalLinks: RouteConfig = { + index: true, + path: '/additional-links', + load: () => import('#views/AdditionalLinks'), + visibility: 'is-anything', +}; + +const manuals: RouteConfig = { + index: true, + path: '/manuals', + load: () => import('#views/Manuals'), + visibility: 'is-anything', +}; + +const policies: RouteConfig = { + index: true, + path: '/policies', + load: () => import('#views/Polices'), + visibility: 'is-anything', +}; + +const guidelines: RouteConfig = { + index: true, + path: '/guidelines', + load: () => import('#views/Guidelines'), + visibility: 'is-anything', +}; + +const onlineInteractive: RouteConfig = { + index: true, + path: '/online-interactive', + load: () => import('#views/OnlineInteractive'), + visibility: 'is-anything', +}; + function child(route: RouteConfig, path: string): RouteConfig { const found = route.children?.find((c) => c.path === path); if (!found) throw new Error(`Child route "${path}" not found in "${route.path}"`); @@ -153,6 +188,11 @@ const routes = { login, reportDetail, capacityAndResourcesDetails, + additionalLinks, + manuals, + guidelines, + onlineInteractive, + policies, // child routes emergencyAlert: child(preparedness, 'emergency-alert'), disasterResponse: child(preparedness, 'disaster-response'), diff --git a/app/components/DocumentCard/index.tsx b/app/components/DocumentCard/index.tsx new file mode 100644 index 0000000..43e65d2 --- /dev/null +++ b/app/components/DocumentCard/index.tsx @@ -0,0 +1,46 @@ +import { + Description, + Heading, + Image, + ListView, +} from '@ifrc-go/ui'; +import { isDefined } from '@togglecorp/fujs'; + +import styles from './styles.module.css'; + +interface DocumentCardProps { + manual: { + title: string; + src: string; + date?: string; + }; +} + +function DocumentCard({ manual }: DocumentCardProps) { + return ( + + + + {manual.title} + + {isDefined(manual.date) && ( + + {manual.date} + + )} + + ); +} + +export default DocumentCard; diff --git a/app/components/DocumentCard/styles.module.css b/app/components/DocumentCard/styles.module.css new file mode 100644 index 0000000..17495b3 --- /dev/null +++ b/app/components/DocumentCard/styles.module.css @@ -0,0 +1,6 @@ +.document-cover { + border: var(--go-ui-width-separator-sm) solid var(--go-ui-color-gray-30); + width: 12.5rem; + height: 12.5rem; + overflow: hidden; +} diff --git a/app/components/Footer/index.tsx b/app/components/Footer/index.tsx index 6085e0a..d87f0ae 100644 --- a/app/components/Footer/index.tsx +++ b/app/components/Footer/index.tsx @@ -92,7 +92,7 @@ function GlobalFooter(props: Props) { - Cookie Policy + Manuals - Terms and Conditions + Policy + + + Guidelines @@ -122,9 +125,8 @@ function GlobalFooter(props: Props) { withSpacingOpticalCorrection > Dataset @@ -134,12 +136,15 @@ function GlobalFooter(props: Props) { Project Mapping Online Interactive + + Additional Links + ('internal'); + + return ( + + + + + Internal Links + + + External Links + + + + + {additionalInternalLinks.map((link) => ( + + + {link.url} + + + ))} + + + + + {additionalExternalLinks.map((link) => ( + + + {link.url} + + + ))} + + + + + ); +} + +export default AdditionalLinks; diff --git a/app/views/Guidelines/index.tsx b/app/views/Guidelines/index.tsx new file mode 100644 index 0000000..cceaf79 --- /dev/null +++ b/app/views/Guidelines/index.tsx @@ -0,0 +1,61 @@ +import { ListView } from '@ifrc-go/ui'; + +import DocumentCard from '#components/DocumentCard'; +import Link from '#components/Link'; +import Page from '#components/Page'; + +// TODO: Fetch the data from server +const guidelines = [ + { + title: 'Title 1', + src: 'https://eoc-ercs.redcrosseth.org/wp-content/uploads/2023/11/1_11zon-276x300.jpg', + }, + { + title: 'Title 2', + src: 'https://eoc-ercs.redcrosseth.org/wp-content/uploads/2023/11/1_11zon-276x300.jpg', + }, + { + title: 'Title 3', + src: 'https://eoc-ercs.redcrosseth.org/wp-content/uploads/2023/11/1_11zon-276x300.jpg', + }, + { + title: 'Title 4', + src: 'https://ercs-1-minio.ifrc-go.dev.togglecorp.com/ercs-media/media/reports/covers/Cholera-outbreak-update-as-of-July-2024-300x135.png', + }, + { + title: 'Title 5', + src: 'https://eoc-ercs.redcrosseth.org/wp-content/uploads/2023/11/Drug.png', + }, + { + title: 'Title 6', + src: 'https://eoc-ercs.redcrosseth.org/wp-content/uploads/2023/11/1_11zon-276x300.jpg', + url: 'http://localhost:8000/media/reports/MDRNP008f_efr.pdf', + }, + +]; + +function Guidelines() { + return ( + + + {guidelines.map((guideline) => ( + + + + ))} + + + ); +} + +export default Guidelines; diff --git a/app/views/Manuals/index.tsx b/app/views/Manuals/index.tsx new file mode 100644 index 0000000..6f9c462 --- /dev/null +++ b/app/views/Manuals/index.tsx @@ -0,0 +1,61 @@ +import { ListView } from '@ifrc-go/ui'; + +import DocumentCard from '#components/DocumentCard'; +import Link from '#components/Link'; +import Page from '#components/Page'; + +// TODO: Fetch the data from server +const manuals = [ + { + title: 'Title 1', + src: 'https://eoc-ercs.redcrosseth.org/wp-content/uploads/2023/11/1_11zon-276x300.jpg', + }, + { + title: 'Title 2', + src: 'https://eoc-ercs.redcrosseth.org/wp-content/uploads/2023/11/1_11zon-276x300.jpg', + }, + { + title: 'Title 3', + src: 'https://eoc-ercs.redcrosseth.org/wp-content/uploads/2023/11/1_11zon-276x300.jpg', + }, + { + title: 'Title 4', + src: 'https://eoc-ercs.redcrosseth.org/wp-content/uploads/2023/11/1_11zon-276x300.jpg', + }, + { + title: 'Title 5', + src: 'https://eoc-ercs.redcrosseth.org/wp-content/uploads/2023/11/1_11zon-276x300.jpg', + }, + { + title: 'Title 6', + src: 'https://eoc-ercs.redcrosseth.org/wp-content/uploads/2023/11/1_11zon-276x300.jpg', + url: 'http://localhost:8000/media/reports/MDRNP008f_efr.pdf', + }, + +]; + +function Manuals() { + return ( + + + {manuals.map((manual) => ( + + + + ))} + + + ); +} + +export default Manuals; diff --git a/app/views/OnlineInteractive/index.tsx b/app/views/OnlineInteractive/index.tsx new file mode 100644 index 0000000..6ae9248 --- /dev/null +++ b/app/views/OnlineInteractive/index.tsx @@ -0,0 +1,72 @@ +import { ListView } from '@ifrc-go/ui'; + +import DocumentCard from '#components/DocumentCard'; +import Link from '#components/Link'; +import Page from '#components/Page'; + +// TODO: Fetch the data from server +const onlineInteractive = [ + { + title: 'Title 1', + src: 'https://eoc-ercs.redcrosseth.org/wp-content/uploads/2023/11/1_11zon-276x300.jpg', + url: 'http://localhost:8000/media/reports/MDRNP008f_efr.pdf', + date: '2024-01-01', + }, + { + title: 'Title 2', + src: 'https://eoc-ercs.redcrosseth.org/wp-content/uploads/2023/11/1_11zon-276x300.jpg', + url: 'http://localhost:8000/media/reports/MDRNP008f_efr.pdf', + date: '2024-01-01', + }, + { + title: 'Title 3', + src: 'https://eoc-ercs.redcrosseth.org/wp-content/uploads/2023/11/1_11zon-276x300.jpg', + url: 'http://localhost:8000/media/reports/MDRNP008f_efr.pdf', + date: '2024-01-01', + }, + { + title: 'Title 4', + src: 'https://eoc-ercs.redcrosseth.org/wp-content/uploads/2023/11/1_11zon-276x300.jpg', + url: 'http://localhost:8000/media/reports/MDRNP008f_efr.pdf', + date: '2024-01-01', + }, + { + title: 'Title 5', + src: 'https://eoc-ercs.redcrosseth.org/wp-content/uploads/2023/11/1_11zon-276x300.jpg', + url: 'http://localhost:8000/media/reports/MDRNP008f_efr.pdf', + date: '2024-01-01', + }, + { + title: 'Title 6', + src: 'https://eoc-ercs.redcrosseth.org/wp-content/uploads/2023/11/1_11zon-276x300.jpg', + url: 'http://localhost:8000/media/reports/MDRNP008f_efr.pdf', + date: '2024-01-01', + }, + +]; + +function OnlineInteractive() { + return ( + + + {onlineInteractive.map((resource) => ( + + + + ))} + + + ); +} + +export default OnlineInteractive; diff --git a/app/views/Polices/index.tsx b/app/views/Polices/index.tsx new file mode 100644 index 0000000..f2d50f5 --- /dev/null +++ b/app/views/Polices/index.tsx @@ -0,0 +1,61 @@ +import { ListView } from '@ifrc-go/ui'; + +import DocumentCard from '#components/DocumentCard'; +import Link from '#components/Link'; +import Page from '#components/Page'; + +// TODO: Fetch the data from server +const policies = [ + { + title: 'Title 1', + src: 'https://eoc-ercs.redcrosseth.org/wp-content/uploads/2023/11/1_11zon-276x300.jpg', + }, + { + title: 'Title 2', + src: 'https://eoc-ercs.redcrosseth.org/wp-content/uploads/2023/11/1_11zon-276x300.jpg', + }, + { + title: 'Title 3', + src: 'https://eoc-ercs.redcrosseth.org/wp-content/uploads/2023/11/1_11zon-276x300.jpg', + }, + { + title: 'Title 4', + src: 'https://eoc-ercs.redcrosseth.org/wp-content/uploads/2023/11/1_11zon-276x300.jpg', + }, + { + title: 'Title 5', + src: 'https://eoc-ercs.redcrosseth.org/wp-content/uploads/2023/11/1_11zon-276x300.jpg', + }, + { + title: 'Title 6', + src: 'https://eoc-ercs.redcrosseth.org/wp-content/uploads/2023/11/1_11zon-276x300.jpg', + url: 'http://localhost:8000/media/reports/MDRNP008f_efr.pdf', + }, + +]; + +function Policies() { + return ( + + + {policies.map((policy) => ( + + + + ))} + + + ); +} + +export default Policies; From b14deba89219a769daf087052dba133c5059c4ef Mon Sep 17 00:00:00 2001 From: crssstha Date: Thu, 28 May 2026 13:41:40 +0545 Subject: [PATCH 2/4] feat(login): added login logic --- README.md | 62 ++++++++++++++++++- app/Root/hooks/useRouteMatching.tsx | 10 ++-- app/Root/index.tsx | 15 +++-- app/components/Navbar/index.tsx | 32 ++++++++-- app/contexts/UserContext.ts | 18 ++++-- app/hooks/useAuth.ts | 20 +++++++ app/views/Galleries/Photos/index.tsx | 4 +- app/views/GuestLayout/index.tsx | 8 +-- app/views/Login/index.tsx | 89 +++++++++++++++++++++++++--- app/views/PrivateLayout/index.tsx | 17 +++++- app/views/RootLayout/index.tsx | 14 ++--- backend | 2 +- 12 files changed, 246 insertions(+), 45 deletions(-) create mode 100644 app/hooks/useAuth.ts diff --git a/README.md b/README.md index 21a5b24..54c61c9 100644 --- a/README.md +++ b/README.md @@ -1 +1,61 @@ -# ERCS ClIENT \ No newline at end of file +# ERCS EOC Platform + +A React application [ERCS EOC](https://eoc-ercs.redcrosseth.org/) website. + +--- + +## Development + +### Prerequisites + +Before you start, create a `.env.local` file: + +```bash +touch .env.local +``` + +Set the following environment variables: + +```env +PORT=3056 +APP_GRAPHQL_ENDPOINT= +APP_GRAPHQL_CODEGEN_ENDPOINT= +APP_TITLE= +APP_ENVIRONMENT="app-env" +APP_MAPBOX_TOKEN="token" +APP_GO_API= +APP_GO_URL= +APP_GO_RISK_API_ENDPOINT= +``` + +--- + +## Local Development (Using Docker, with Backend) + +This project uses Git submodules. After cloning, initialize and update them: + +```bash +git submodule update --init --recursive +``` + +We use a `docker-compose.yml` file located at `./backend/docker-compose.yml`. + +To run it, add the following to your `.env.local`: + +```env +# Include the backend services +COMPOSE_FILE=./backend/docker-compose.yaml:docker-compose.yml +# Use the same .env file for both backend and web-app +BACKEND_ENV_FILE=../.env +``` + +Then start the full stack: + +```bash +docker compose up +``` + +> **Note:** `../` refers to the ERCS EOC Platform folder, relative to `./backend/docker-compose.yml` +> (the main Docker Compose file). + +--- \ No newline at end of file diff --git a/app/Root/hooks/useRouteMatching.tsx b/app/Root/hooks/useRouteMatching.tsx index a2b2a5a..b7d763b 100644 --- a/app/Root/hooks/useRouteMatching.tsx +++ b/app/Root/hooks/useRouteMatching.tsx @@ -1,7 +1,6 @@ -import { use } from 'react'; import { generatePath } from 'react-router'; -import UserContext from '#contexts/UserContext'; +import useAuth from '#hooks/useAuth'; import type { RouteKeys } from '#root/config/routes'; import routes from '#root/config/routes'; @@ -10,8 +9,7 @@ export interface Attrs { } function useRouteMatching(routeKey: RouteKeys, attrs?: Attrs) { - const { authenticated } = use(UserContext); - + const { isAuthenticated } = useAuth(); const to = routes[routeKey]; if (!to) { @@ -23,11 +21,11 @@ function useRouteMatching(routeKey: RouteKeys, attrs?: Attrs) { path, } = to; - if (visibility === 'is-not-authenticated' && authenticated) { + if (visibility === 'is-not-authenticated' && isAuthenticated) { return undefined; } - if (visibility === 'is-authenticated' && !authenticated) { + if (visibility === 'is-authenticated' && !isAuthenticated) { return undefined; } diff --git a/app/Root/index.tsx b/app/Root/index.tsx index ee570ab..9ec620f 100644 --- a/app/Root/index.tsx +++ b/app/Root/index.tsx @@ -53,12 +53,20 @@ const gqlClient = new Client({ function Root() { const [user, setUser] = useState(); - const authenticated = !!user; + const [isAuthLoading, setIsAuthLoading] = useState(true); + + const removeUserAuth = (() => { + setUser(undefined); + setIsAuthLoading(false); + }); + const userContext: UserContextInterface = useMemo(() => ({ - authenticated, user, setUser, - }), [authenticated, user]); + removeUserAuth, + isAuthLoading, + setIsAuthLoading, + }), [user, isAuthLoading]); const requestContextValue = useMemo(() => ({ transformUrl: processGoUrls, @@ -88,7 +96,6 @@ function Root() { - ); } diff --git a/app/components/Navbar/index.tsx b/app/components/Navbar/index.tsx index 7527077..bd2888f 100644 --- a/app/components/Navbar/index.tsx +++ b/app/components/Navbar/index.tsx @@ -8,21 +8,41 @@ import { NavigationTabList, PageContainer, } from '@ifrc-go/ui'; +import { gql } from 'urql'; import Link from '#components/Link'; import NavLink from '#components/NavLink'; import UserContext from '#contexts/UserContext'; +import { useLogoutMutation } from '#generated/types/graphql'; +import useAlert from '#hooks/useAlert'; +import useAuth from '#hooks/useAuth'; import Logo from '#resources/image/logo.png'; import styles from './styles.module.css'; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const LOGOUT_MUTATION = gql` + mutation Logout { + logout + } +`; + function Navbar() { - const { authenticated } = use(UserContext); + const [{ fetching: logoutPending }, triggerLogout] = useLogoutMutation(); + const { removeUserAuth } = use(UserContext); + const { isAuthenticated } = useAuth(); const navigate = useNavigate(); + const alert = useAlert(); const handleLogin = () => { navigate('/login'); }; + + const handleLogout = async () => { + await triggerLogout({}); + removeUserAuth(); + alert.show('Logout successful!', { variant: 'success' }); + }; return (