From db94038be80b527372c78d17d8173e369b354eca Mon Sep 17 00:00:00 2001 From: barshathakuri Date: Tue, 26 Mar 2024 16:56:46 +0545 Subject: [PATCH] Add Footer and Map in home page --- env.ts | 1 + package.json | 4 +- src/App/index.tsx | 4 + src/App/routes.tsx | 4 + src/assets/icons/go-logo-2020.svg | 5 +- src/components/GlobalFooter/i18n.json | 16 ++ src/components/GlobalFooter/index.tsx | 132 +++++++++++++ src/components/GlobalFooter/styles.module.css | 53 ++++++ src/components/Navbar/i18n.json | 4 +- src/components/Navbar/index.tsx | 26 ++- src/components/Navbar/styles.module.css | 33 +++- src/components/Page/i18n.json | 6 + src/components/Page/index.tsx | 150 +++++++++++++++ src/components/Page/styles.module.css | 29 +++ src/components/domain/BaseMap/index.tsx | 113 +++++++++++ src/config.ts | 20 ++ src/hooks/domain/useCurrentLanguage.ts | 10 + src/hooks/useBooleanState.ts | 24 --- src/index.tsx | 1 + src/utils/constants.ts | 45 +++++ src/utils/map.ts | 178 ++++++++++++++++++ src/views/AlertMap/i18n.json | 3 +- src/views/AlertMap/index.tsx | 47 ++++- src/views/AlertMap/styles.module.css | 11 +- src/views/Home/index.tsx | 7 +- src/views/Home/styles.module.css | 4 + src/views/RootLayout/index.tsx | 4 + tsconfig.json | 3 - vite.config.ts | 7 + 29 files changed, 902 insertions(+), 42 deletions(-) create mode 100644 src/components/GlobalFooter/i18n.json create mode 100644 src/components/GlobalFooter/index.tsx create mode 100644 src/components/GlobalFooter/styles.module.css create mode 100644 src/components/Page/i18n.json create mode 100644 src/components/Page/index.tsx create mode 100644 src/components/Page/styles.module.css create mode 100644 src/components/domain/BaseMap/index.tsx create mode 100644 src/config.ts create mode 100644 src/hooks/domain/useCurrentLanguage.ts delete mode 100644 src/hooks/useBooleanState.ts create mode 100644 src/utils/constants.ts create mode 100644 src/utils/map.ts diff --git a/env.ts b/env.ts index fa920854..db9619d9 100644 --- a/env.ts +++ b/env.ts @@ -2,4 +2,5 @@ import { defineConfig, Schema } from '@julr/vite-plugin-validate-env'; export default defineConfig({ APP_TITLE: Schema.string.optional(), + APP_MAPBOX_ACCESS_TOKEN: Schema.string(), }) diff --git a/package.json b/package.json index 3d6a7186..01866f3d 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@togglecorp/re-map": "^0.1.4", "@turf/bbox": "^6.5.0", "graphql": "^16.8.1", - "mapbox-gl": "^1.13.0", + "mapbox-gl": "^3.2.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.22.3" @@ -35,7 +35,7 @@ "devDependencies": { "@eslint/eslintrc": "^2.0.3", "@julr/vite-plugin-validate-env": "^1.0.1", - "@types/mapbox-gl": "^1.13.0", + "@types/mapbox-gl": "^3.1.0", "@types/node": "^20.1.3", "@types/react": "^18.0.28", "@types/react-dom": "^18.2.22", diff --git a/src/App/index.tsx b/src/App/index.tsx index 63f6529f..b4751b15 100644 --- a/src/App/index.tsx +++ b/src/App/index.tsx @@ -2,10 +2,14 @@ import { createBrowserRouter, RouterProvider, } from 'react-router-dom'; +import mapboxgl from 'mapbox-gl'; + +import { mbtoken } from '#config'; import { unwrappedRoutes } from './routes'; const router = createBrowserRouter(unwrappedRoutes); +mapboxgl.accessToken = mbtoken; function App() { return ( diff --git a/src/App/routes.tsx b/src/App/routes.tsx index a3749bb9..62c5e92a 100644 --- a/src/App/routes.tsx +++ b/src/App/routes.tsx @@ -57,3 +57,7 @@ export const wrappedRoutes = { }; export const unwrappedRoutes = unwrapRoute(Object.values(wrappedRoutes)); + +export default wrappedRoutes; + +export type WrappedRoutes = typeof wrappedRoutes; diff --git a/src/assets/icons/go-logo-2020.svg b/src/assets/icons/go-logo-2020.svg index aca6329b..54a8241e 100644 --- a/src/assets/icons/go-logo-2020.svg +++ b/src/assets/icons/go-logo-2020.svg @@ -13,10 +13,10 @@ id="Layer_1" x="0px" y="0px" - viewBox="0 0 1480 350" + viewBox="0 0 1000 350" xml:space="preserve" sodipodi:docname="go-logo-2020.svg" - width="1480" + width="1000" height="350" inkscape:version="0.92.4 (f8dce91, 2019-08-02)"> + \ No newline at end of file diff --git a/src/components/GlobalFooter/i18n.json b/src/components/GlobalFooter/i18n.json new file mode 100644 index 00000000..d49c5891 --- /dev/null +++ b/src/components/GlobalFooter/i18n.json @@ -0,0 +1,16 @@ +{ + "namespace": "common", + "strings": { + "footerAboutAlertHub":"About Alert Hub", + "footerAboutAlertHubDesc":"The aim of IFRC Alert Hub is to provide timely and effective emergency alerts to communities worldwide, empowering them to protect their lives and livelihoods.", + "footerFindOutMore":"Find out more", + "footerHelpfulLinks":"Helpful links", + "footerOpenSourceCode":"Open Source Code", + "footerApiDocumentation":"API Documentation", + "footerOtherResources":"Other Resources", + "footerContactUs":"Contact Us", + "footerIFRC":"© IFRC {year} v{appVersion}", + "globalFindOut": "Find Out More", + "globalHelpfulLinks": "Helpful links" + } +} diff --git a/src/components/GlobalFooter/index.tsx b/src/components/GlobalFooter/index.tsx new file mode 100644 index 00000000..6cdc2d0f --- /dev/null +++ b/src/components/GlobalFooter/index.tsx @@ -0,0 +1,132 @@ +import { Link } from 'react-router-dom'; +import { + Heading, + PageContainer, +} from '@ifrc-go/ui'; +import { useTranslation } from '@ifrc-go/ui/hooks'; +import { resolveToComponent } from '@ifrc-go/ui/utils'; +import { _cs } from '@togglecorp/fujs'; + +import { + appCommitHash, + appVersion, +} from '#config'; + +import i18n from './i18n.json'; +import styles from './styles.module.css'; + +const date = new Date(); +const year = date.getFullYear(); + +interface Props { + className?: string; +} + +function GlobalFooter(props: Props) { + const { + className, + } = props; + + const strings = useTranslation(i18n); + const copyrightText = resolveToComponent( + strings.footerIFRC, + { + year, + appVersion: ( + + {appVersion} + + ), + }, + ); + + return ( + +
+ + {strings.footerAboutAlertHub} + +
+ {strings.footerAboutAlertHubDesc} +
+
+ {copyrightText} +
+
+
+ + {strings.globalFindOut} + +
+ + ifrc.org + + + rcrcsims.org + + + data.ifrc.org + +
+
+
+ + {strings.globalHelpfulLinks} + +
+ + {strings.footerOpenSourceCode} + + + {strings.footerApiDocumentation} + + {/* // FIXME: Add Resource route */} + + {strings.footerOtherResources} + +
+
+
+ + {strings.footerContactUs} + + + im@ifrc.org + +
+
+ ); +} + +export default GlobalFooter; diff --git a/src/components/GlobalFooter/styles.module.css b/src/components/GlobalFooter/styles.module.css new file mode 100644 index 00000000..a8444478 --- /dev/null +++ b/src/components/GlobalFooter/styles.module.css @@ -0,0 +1,53 @@ +.footer { + background-color: var(--go-ui-color-primary-gray); + color: var(--go-ui-color-white); + + .content { + display: flex; + gap: var(--go-ui-spacing-2xl); + flex-wrap: wrap; + + .section { + display: flex; + flex-basis: 12rem; + flex-direction: column; + flex-grow: 1; + gap: var(--go-ui-spacing-lg); + + .sub-section { + display: flex; + flex-direction: column; + gap: var(--go-ui-spacing-sm); + + .footer-link { + text-decoration: none; + color: var(--go-ui-color-white); + font-weight: var(--go-ui-font-weight-medium); + } + + .footer-link:hover { + text-decoration: underline; + color: var(--go-ui-color-red-hover); + } + } + + .contact-button { + border-radius: var(--go-ui-border-radius-2xl); + background-color: var(--go-ui-color-primary-red); + padding: var(--go-ui-spacing-2xs) var(--go-ui-spacing-xl); + width: fit-content; + text-decoration: none; + color: var(--go-ui-color-white); + font-weight: var(--go-ui-font-weight-medium); + } + + .contact-button:hover { + text-decoration: underline; + } + } + } + + @media print { + display: none; + } +} \ No newline at end of file diff --git a/src/components/Navbar/i18n.json b/src/components/Navbar/i18n.json index 97bf920f..7ab752a8 100644 --- a/src/components/Navbar/i18n.json +++ b/src/components/Navbar/i18n.json @@ -5,6 +5,8 @@ "headerLogoAltText": "Alert Hub logo", "appLogin": "Login", "appAbout": "About", - "appResources": "Resources" + "appResources": "Resources", + "headerMenuHome": "Home", + "headerMenuMySubscription": "My Subscription" } } \ No newline at end of file diff --git a/src/components/Navbar/index.tsx b/src/components/Navbar/index.tsx index cff71caa..bcc49d28 100644 --- a/src/components/Navbar/index.tsx +++ b/src/components/Navbar/index.tsx @@ -1,6 +1,7 @@ import { Link } from 'react-router-dom'; import { Button, + Heading, NavigationTabList, PageContainer, } from '@ifrc-go/ui'; @@ -32,12 +33,20 @@ function Navbar(props: Props) { contentClassName={styles.topContent} >
- + {strings.headerLogoAltText} + + ALERT HUB +
+ + + + {strings.headerMenuHome} + + + ); } diff --git a/src/components/Navbar/styles.module.css b/src/components/Navbar/styles.module.css index 821a044d..53835175 100644 --- a/src/components/Navbar/styles.module.css +++ b/src/components/Navbar/styles.module.css @@ -22,6 +22,12 @@ .go-icon { height: var(--go-ui-height-brand-icon); } + .alert-hub-title { + display: flex; + align-items: center; + text-decoration: none; + color: var(--go-ui-color-text); + } } .actions { @@ -31,10 +37,33 @@ .action-item { text-decoration: none; - font-weight: var(--go-ui-font-weight-medium); color: var(--go-ui-color-text); + font-weight: var(--go-ui-font-weight-medium); + } + + .action-item:hover { + text-decoration: underline; + color: var(--go-ui-color-primary-red); } } } } -} + + .bottom { + display: flex; + flex-wrap: wrap; + gap: var(--go-ui-spacing-sm) var(--go-ui-spacing-lg); + padding: var(--go-ui-spacing-md) var(--go-ui-spacing-lg); + + .menu-item { + text-decoration: underline; + color: var(--go-ui-color-primary-red); + font-weight: var(--go-ui-font-weight-medium); + } + + .search-container { + flex-basis: 30rem; + flex-grow: 0.5; + } + } +} \ No newline at end of file diff --git a/src/components/Page/i18n.json b/src/components/Page/i18n.json new file mode 100644 index 00000000..f3f0087b --- /dev/null +++ b/src/components/Page/i18n.json @@ -0,0 +1,6 @@ +{ + "namespace": "common", + "strings": { + "machineTranslatedContentWarning": "The contents in this page was machine translated from {contentOriginalLanguage}!" + } +} diff --git a/src/components/Page/index.tsx b/src/components/Page/index.tsx new file mode 100644 index 00000000..97bab9d5 --- /dev/null +++ b/src/components/Page/index.tsx @@ -0,0 +1,150 @@ +import { + ElementRef, + RefObject, + useEffect, +} from 'react'; +import { + PageContainer, + PageHeader, +} from '@ifrc-go/ui'; +import { type Language } from '@ifrc-go/ui/contexts'; +import { useTranslation } from '@ifrc-go/ui/hooks'; +import { + languageNameMapEn, + resolveToString, +} from '@ifrc-go/ui/utils'; +import { + _cs, + isDefined, + isNotDefined, +} from '@togglecorp/fujs'; + +import useCurrentLanguage from '#hooks/domain/useCurrentLanguage'; + +import i18n from './i18n.json'; +import styles from './styles.module.css'; + +type TranslationModuleOriginalLanguageEnum = components<'read'>['schemas']['TranslationModuleOriginalLanguageEnum']; + +interface Props { + className?: string; + title?: string; + actions?: React.ReactNode; + heading?: React.ReactNode; + description?: React.ReactNode; + descriptionContainerClassName?: string; + mainSectionContainerClassName?: string; + breadCrumbs?: React.ReactNode; + info?: React.ReactNode; + children?: React.ReactNode; + mainSectionClassName?: string; + infoContainerClassName?: string; + wikiLink?: React.ReactNode; + withBackgroundColorInMainSection?: boolean; + elementRef?: RefObject>; + blockingContent?: React.ReactNode; + contentOriginalLanguage?: TranslationModuleOriginalLanguageEnum; + beforeHeaderContent?: React.ReactNode; +} + +function Page(props: Props) { + const { + className, + title, + actions, + heading, + description, + descriptionContainerClassName, + breadCrumbs, + info, + children, + mainSectionContainerClassName, + mainSectionClassName, + infoContainerClassName, + wikiLink, + withBackgroundColorInMainSection, + elementRef, + blockingContent, + contentOriginalLanguage, + beforeHeaderContent, + } = props; + + const currentLanguage = useCurrentLanguage(); + const strings = useTranslation(i18n); + + useEffect(() => { + if (isDefined(title)) { + document.title = title; + } + }, [title]); + + const showMachineTranslationWarning = isDefined(contentOriginalLanguage) + && contentOriginalLanguage !== currentLanguage; + + const showPageContainer = !!breadCrumbs + || !!heading + || !!description + || !!info + || !!actions + || !!wikiLink; + + return ( +
+ {isNotDefined(blockingContent) + && showMachineTranslationWarning + && ( +
+ {resolveToString( + strings.machineTranslatedContentWarning, + // eslint-disable-next-line max-len + { contentOriginalLanguage: languageNameMapEn[contentOriginalLanguage as Language] }, + )} +
+ )} + {beforeHeaderContent && ( + + {beforeHeaderContent} + + )} + {isNotDefined(blockingContent) && showPageContainer && ( + + )} + {isNotDefined(blockingContent) && ( + + { children } + + )} +
+ ); +} + +export default Page; diff --git a/src/components/Page/styles.module.css b/src/components/Page/styles.module.css new file mode 100644 index 00000000..3f31d154 --- /dev/null +++ b/src/components/Page/styles.module.css @@ -0,0 +1,29 @@ +.page { + display: flex; + flex-direction: column; + flex-grow: 1; + + .machine-translation-warning { + background-color: var(--go-ui-color-warning); + padding: var(--go-ui-spacing-sm); + text-align: center; + color: var(--go-ui-color-white); + } + + .page-header { + background-color: var(--go-ui-color-background); + } + + .main-section-container { + flex-grow: 1; + background-color: var(--go-ui-color-white); + + &.with-background-color { + background-color: var(--go-ui-color-background); + } + + .main-section { + padding: var(--go-ui-spacing-2xl) var(--go-ui-spacing-lg); + } + } +} diff --git a/src/components/domain/BaseMap/index.tsx b/src/components/domain/BaseMap/index.tsx new file mode 100644 index 00000000..25335161 --- /dev/null +++ b/src/components/domain/BaseMap/index.tsx @@ -0,0 +1,113 @@ +import Map, { + MapLayer, + MapSource, +} from '@togglecorp/re-map'; +import { type SymbolLayer } from 'mapbox-gl'; + +import { + adminLabelLayerOptions, + defaultMapOptions, + defaultMapStyle, + defaultNavControlOptions, + defaultNavControlPosition, +} from '#utils/map'; + +type MapProps = Parameters[0]; + +type overrides = 'mapStyle' | 'mapOptions' | 'navControlShown' | 'navControlPosition' | 'navControlOptions' | 'scaleControlShown'; + +type BaseMapProps = Omit & { + baseLayers?: React.ReactNode; + withDisclaimer?: boolean; + withoutLabel?: boolean; +} & Partial>; + +const sourceOptions: mapboxgl.GeoJSONSourceRaw = { + type: 'geojson', +}; + +const adminLabelOverrideOptions: Omit = { + type: 'symbol', + layout: { + 'text-field': ['get', 'name'], + 'text-font': ['Poppins Regular', 'Arial Unicode MS Regular'], + 'text-letter-spacing': 0.15, + 'text-line-height': 1.2, + 'text-max-width': 8, + 'text-justify': 'center', + 'text-anchor': 'top', + 'text-padding': 2, + 'text-size': [ + 'interpolate', ['linear', 1], ['zoom'], + 0, 6, + 6, 16, + ], + }, + paint: { + 'text-color': '#000000', + 'text-halo-color': '#555555', + 'text-halo-width': 0.2, + }, +}; + +function BaseMap(props: BaseMapProps) { + const { + baseLayers, + mapStyle, + mapOptions, + navControlShown, + navControlPosition, + navControlOptions, + scaleControlShown, + children, + withoutLabel = false, + ...otherProps + } = props; + + return ( + + + + + + {baseLayers} + + {!withoutLabel && ( + + + + )} + {children} + + ); +} + +export default BaseMap; diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 00000000..438f0822 --- /dev/null +++ b/src/config.ts @@ -0,0 +1,20 @@ +const { + APP_ENVIRONMENT, + APP_API_ENDPOINT, + APP_ADMIN_URL, + APP_TITLE, + APP_COMMIT_HASH, + APP_VERSION, + APP_MAPBOX_ACCESS_TOKEN, +} = import.meta.env; + +export const environment = APP_ENVIRONMENT; + +export const appTitle = APP_TITLE; +export const appCommitHash = APP_COMMIT_HASH; +export const appVersion = APP_VERSION; + +export const api = APP_API_ENDPOINT; +export const adminUrl = APP_ADMIN_URL ?? api; + +export const mbtoken = APP_MAPBOX_ACCESS_TOKEN; diff --git a/src/hooks/domain/useCurrentLanguage.ts b/src/hooks/domain/useCurrentLanguage.ts new file mode 100644 index 00000000..7aca9307 --- /dev/null +++ b/src/hooks/domain/useCurrentLanguage.ts @@ -0,0 +1,10 @@ +import { useContext } from 'react'; +import { LanguageContext } from '@ifrc-go/ui/contexts'; + +function useCurrentLanguage() { + const { currentLanguage } = useContext(LanguageContext); + + return currentLanguage; +} + +export default useCurrentLanguage; diff --git a/src/hooks/useBooleanState.ts b/src/hooks/useBooleanState.ts deleted file mode 100644 index 3ebf05e6..00000000 --- a/src/hooks/useBooleanState.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { - useCallback, - useState, -} from 'react'; - -function useBooleanState(defaultValue?: boolean) { - const [value, setValue] = useState(!!defaultValue); - - const setTrue = useCallback(() => setValue(true), []); - const setFalse = useCallback(() => setValue(false), []); - const toggle = useCallback(() => setValue((x) => !x), []); - - return [ - value, - { - setValue, - setTrue, - setFalse, - toggle, - }, - ] as const; -} - -export default useBooleanState; diff --git a/src/index.tsx b/src/index.tsx index d974f07e..a54627c1 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,4 +1,5 @@ import './index.css'; +import 'mapbox-gl/dist/mapbox-gl.css'; import React from 'react'; import ReactDOM from 'react-dom/client'; diff --git a/src/utils/constants.ts b/src/utils/constants.ts new file mode 100644 index 00000000..d682d08c --- /dev/null +++ b/src/utils/constants.ts @@ -0,0 +1,45 @@ +export const defaultChartMargin = { + top: 0, + right: 0, + bottom: 0, + left: 0, +}; + +export const defaultChartPadding = { + top: 10, + right: 10, + bottom: 10, + left: 10, +}; + +// Map +export const DURATION_MAP_ZOOM = 1000; +export const DEFAULT_MAP_PADDING = 50; + +// Storage + +export const KEY_USER_STORAGE = 'user'; +export const KEY_LANGUAGE_STORAGE = 'language'; + +// Search page + +export const KEY_URL_SEARCH = 'keyword'; +export const SEARCH_TEXT_LENGTH_MIN = 3; + +// Colors + +export const COLOR_WHITE = '#ffffff'; +export const COLOR_TEXT = '#313131'; +export const COLOR_TEXT_ON_DARK = COLOR_WHITE; +export const COLOR_LIGHT_GREY = '#e0e0e0'; +export const COLOR_DARK_GREY = '#a5a5a5'; +export const COLOR_BLACK = '#000000'; +export const COLOR_LIGHT_YELLOW = '#ffd470'; +export const COLOR_YELLOW = '#ff9e00'; +export const COLOR_BLUE = '#4c5d9b'; +export const COLOR_LIGHT_BLUE = '#c7d3e0'; +export const COLOR_ORANGE = '#ff8000'; +export const COLOR_RED = '#f5333f'; +export const COLOR_DARK_RED = '#730413'; +export const COLOR_PRIMARY_BLUE = '#011e41'; +export const COLOR_PRIMARY_RED = '#f5333f'; diff --git a/src/utils/map.ts b/src/utils/map.ts new file mode 100644 index 00000000..39180676 --- /dev/null +++ b/src/utils/map.ts @@ -0,0 +1,178 @@ +import getBbox from '@turf/bbox'; +import type { + FillLayer, + Map, + NavigationControl, + SymbolLayer, +} from 'mapbox-gl'; + +import { + COLOR_DARK_GREY, + COLOR_LIGHT_GREY, +} from '#utils/constants'; + +export const defaultMapStyle = 'mapbox://styles/go-ifrc/ckrfe16ru4c8718phmckdfjh0'; +type NavControlOptions = NonNullable[0]>; +export const defaultNavControlOptions: NavControlOptions = { + showCompass: false, +}; + +type ControlPosition = NonNullable[1]>; +export const defaultNavControlPosition: ControlPosition = 'top-right'; + +export const defaultMapOptions: Omit = { + logoPosition: 'bottom-left' as const, + zoom: 1.5, + minZoom: 1, + maxZoom: 18, + scrollZoom: false, + pitchWithRotate: false, + dragRotate: false, + renderWorldCopies: true, + attributionControl: false, + preserveDrawingBuffer: true, + // interactive: false, +}; + +const DEFAULT_CIRCLE_SIZE = 'medium'; +const DEFAULT_CIRCLE_OPACITY = 'full'; + +export const CIRCLE_RADIUS_SMALL = 3; +export const CIRCLE_RADIUS_MEDIUM = 5; +export const CIRCLE_RADIUS_LARGE = 8; +export const CIRCLE_RADIUS_EXTRA_LARGE = 12; +export const CIRCLE_RADIUS_SUPER_LARGE = 16; + +export function getPointCirclePaint( + color: string, + size: 'small' | 'medium' | 'large' | 'extraLarge' = DEFAULT_CIRCLE_SIZE, + opacity: 'full' | 'light' = DEFAULT_CIRCLE_OPACITY, +): mapboxgl.CirclePaint { + const sizeMap = { + small: CIRCLE_RADIUS_SMALL, + medium: CIRCLE_RADIUS_MEDIUM, + large: CIRCLE_RADIUS_LARGE, + extraLarge: CIRCLE_RADIUS_EXTRA_LARGE, + }; + + const opacityMap = { + full: 1, + light: 0.7, + }; + + return { + 'circle-color': color, + 'circle-radius': sizeMap[size] ?? DEFAULT_CIRCLE_SIZE, + 'circle-opacity': opacityMap[opacity] ?? DEFAULT_CIRCLE_OPACITY, + 'circle-pitch-alignment': 'map', + }; +} + +export function getPointCircleHaloPaint( + color: string, + scaleProp: string, + maxScaleValue: number, +): mapboxgl.CirclePaint { + // NOTE: setting this value as 2 because there are already stops of 0 + // and 1 + const maxScale = Math.max(maxScaleValue, 2); + + return { + ...getPointCirclePaint(color), + 'circle-opacity': 0.4, + 'circle-radius': [ + 'interpolate', + ['linear'], + ['zoom'], + 3, [ + 'interpolate', + ['exponential', 1], + ['number', ['get', scaleProp]], + 0, + 0, + 1, + 10, + maxScale, + 15, + ], + 8, [ + 'interpolate', + ['exponential', 1], + ['number', ['get', scaleProp]], + 0, + 0, + 1, + 20, + maxScale, + 40, + ], + ], + }; +} + +export const defaultTooltipOptions: mapboxgl.PopupOptions = { + closeButton: false, + offset: 10, +}; + +export const adminLabelLayerOptions : Omit = { + type: 'symbol', + layout: { + visibility: 'none', + }, +}; + +export const adminLabelOverrideOptions: Omit = { + type: 'symbol', + layout: { + 'text-field': ['get', 'name'], + 'text-font': ['Poppins Regular', 'Arial Unicode MS Regular'], + 'text-letter-spacing': 0.15, + 'text-line-height': 1.2, + 'text-max-width': 8, + 'text-justify': 'center', + 'text-anchor': 'top', + 'text-padding': 2, + 'text-size': [ + 'interpolate', ['linear', 1], ['zoom'], + 0, 6, + 6, 16, + ], + }, + paint: { + 'text-color': '#000000', + 'text-halo-color': '#000000', + 'text-halo-width': 0.2, + }, +}; + +export const adminFillLayerOptions: Omit = { + type: 'fill', + layout: { + visibility: 'visible', + }, + paint: { + 'fill-color': [ + 'case', + ['boolean', ['feature-state', 'hovered'], false], + COLOR_DARK_GREY, + COLOR_LIGHT_GREY, + ], + }, +}; + +export function getCountryListBoundingBox(countryList: Country[]) { + if (countryList.length < 1) { + return undefined; + } + + const collection = { + type: 'FeatureCollection' as const, + features: countryList.map((country) => ({ + type: 'Feature' as const, + geometry: country.bbox, + })), + }; + + return getBbox(collection); +} diff --git a/src/views/AlertMap/i18n.json b/src/views/AlertMap/i18n.json index 60c6a445..8adb4bb7 100644 --- a/src/views/AlertMap/i18n.json +++ b/src/views/AlertMap/i18n.json @@ -1,6 +1,7 @@ { "namespace": "ongoingAlertMap", "strings": { - "mapHeading": "All Ongoing Alerts" + "mapHeading": "All Ongoing Alerts", + "mapViewAllSources": "View All Sources" } } \ No newline at end of file diff --git a/src/views/AlertMap/index.tsx b/src/views/AlertMap/index.tsx index 4c83ec6a..1d256356 100644 --- a/src/views/AlertMap/index.tsx +++ b/src/views/AlertMap/index.tsx @@ -1,18 +1,33 @@ +import { Link } from 'react-router-dom'; +import { Container } from '@ifrc-go/ui'; import { useTranslation } from '@ifrc-go/ui/hooks'; import { _cs } from '@togglecorp/fujs'; +import { + MapBounds, + MapContainer, + MapLayer, +} from '@togglecorp/re-map'; +import type { LngLatBoundsLike } from 'mapbox-gl'; -import Container from '#ui/Container'; +import BaseMap from '#components/domain/BaseMap'; +import { + DEFAULT_MAP_PADDING, + DURATION_MAP_ZOOM, +} from '#utils/constants'; +import { adminFillLayerOptions } from '#utils/map'; import i18n from './i18n.json'; import styles from './styles.module.css'; type Props = { className?: string; + bbox: LngLatBoundsLike | undefined; } function OngoingAlertMap(props: Props) { const { className, + bbox, } = props; const strings = useTranslation(i18n); @@ -20,10 +35,36 @@ function OngoingAlertMap(props: Props) { return ( + {strings.mapViewAllSources} + + )} > - Map + + )} + > + + + ); } diff --git a/src/views/AlertMap/styles.module.css b/src/views/AlertMap/styles.module.css index bed4a1d1..5f69ed1c 100644 --- a/src/views/AlertMap/styles.module.css +++ b/src/views/AlertMap/styles.module.css @@ -3,7 +3,16 @@ position: relative; } + .sources { + color: var(--go-ui-color-text); + font-weight: var(--go-ui-font-weight-medium); + } + + .sources:hover { + color: var(--go-ui-color-primary-red); + } + .map-container { height: 40rem; } -} +} \ No newline at end of file diff --git a/src/views/Home/index.tsx b/src/views/Home/index.tsx index c24473ab..7ea2cdcd 100644 --- a/src/views/Home/index.tsx +++ b/src/views/Home/index.tsx @@ -1,6 +1,6 @@ import { useTranslation } from '@ifrc-go/ui/hooks'; -import Page from '#ui/Page'; +import Page from '#components/Page'; import OngoingAlertMap from '../AlertMap'; @@ -17,9 +17,12 @@ export function Component() { className={styles.home} heading={strings.homeHeading} description={strings.homeDescription} + descriptionContainerClassName={styles.headingDescription} mainSectionClassName={styles.content} > - + ); } diff --git a/src/views/Home/styles.module.css b/src/views/Home/styles.module.css index 25a65936..9b6e67e0 100644 --- a/src/views/Home/styles.module.css +++ b/src/views/Home/styles.module.css @@ -4,4 +4,8 @@ flex-direction: column; gap: var(--go-ui-spacing-2xl); } + .heading-description { + max-width: var(--go-ui-page-header-description-width-max); + text-align: center; + } } diff --git a/src/views/RootLayout/index.tsx b/src/views/RootLayout/index.tsx index 4a81e5c0..c82bcb65 100644 --- a/src/views/RootLayout/index.tsx +++ b/src/views/RootLayout/index.tsx @@ -5,6 +5,7 @@ import { import { AlertContainer } from '@ifrc-go/ui'; import { _cs } from '@togglecorp/fujs'; +import GlobalFooter from '#components/GlobalFooter'; import Navbar from '#components/Navbar'; import useDebouncedValue from '#hooks/useDebouncedValue'; @@ -30,6 +31,9 @@ export function Component() {
+ ); diff --git a/tsconfig.json b/tsconfig.json index b3f1b069..dc373cd6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,16 +1,13 @@ { "compilerOptions": { "paths": { - "#generated/*": ["./generated/*"], "#assets/*": ["./src/assets/*"], "#components/*": ["./src/components/*"], "#config": ["./src/config"], "#contexts/*": ["./src/contexts/*"], "#hooks/*": ["./src/hooks/*"], - "#strings/*": ["./src/strings/*"], "#utils/*": ["./src/utils/*"], "#views/*": ["./src/views/*"], - "#ui/*": ["./src/ui/src/components/*"], }, "target": "ESNext", diff --git a/vite.config.ts b/vite.config.ts index f0c85856..4961000a 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -49,6 +49,13 @@ export default defineConfig(({ mode }) => { build: { outDir: 'build', sourcemap: isProd, + rollupOptions: { + output: { + manualChunks: { + 'mapbox-gl': ['mapbox-gl'], + } + }, + }, }, test: { environment: 'happy-dom',