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

Render icon per site type #271

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ repos:
"[email protected]",
"[email protected]",
"[email protected]",
"eslint-plugin-unicorn@49.0",
"eslint-plugin-unicorn@55.0",
"[email protected]",
# Transitive dependencies of ESLint-Config-Beslogic
"@typescript-eslint/[email protected]", # dependency of typescript-eslint
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ def create_tree_types(self):
)

def create_site_types(self):
# This mapping MUST MATCH pinMap in canopeum_frontend/src/pages/Map.tsx
# This mapping MUST MATCH pinMap in canopeum_frontend/src/models/SiteType.ts
site_type_names = {
1: ("Canopeum", "Canopeum"),
2: ("Parks", "Parcs"),
Expand Down
30 changes: 30 additions & 0 deletions canopeum_frontend/src/components/IconBadge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { MaterialIcon } from 'material-icons'

import type { SiteTypeIconKey } from '@models/SiteType'

type Props = {
readonly iconKey: MaterialIcon | SiteTypeIconKey,
readonly bgColor?: string,
}

const IconBadge = (props: Props) => {
const bgColor = props.bgColor === ''
? ''
: `text-bg-${props.bgColor ?? 'primary'}`

return (
<div
className={`${bgColor} text-center rounded-circle`}
style={{ height: '2em', width: '2em' }}
>
<span
className='material-symbols-outlined text-light align-middle'
style={{ fontSize: 24, marginTop: '0.15em' }}
>
{props.iconKey}
</span>
</div>
)
}

export default IconBadge
21 changes: 0 additions & 21 deletions canopeum_frontend/src/components/PrimaryIconBadge.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import { Link } from 'react-router-dom'
import SiteSponsorProgress from '@components/analytics/SiteSponsorProgress'
import SiteSummaryActions from '@components/analytics/SiteSummaryActions'
import { AuthenticationContext } from '@components/context/AuthenticationContext'
import IconBadge from '@components/IconBadge'
import CustomIcon from '@components/icons/CustomIcon'
import PrimaryIconBadge from '@components/PrimaryIconBadge'
import { appRoutes } from '@constants/routes.constant'
import { getSiteTypeIconKey } from '@models/SiteType'
import type { SiteSummary, User } from '@services/api'

type Props = {
Expand Down Expand Up @@ -35,7 +36,7 @@ const SiteSummaryCard = ({ site, admins, onSiteChange, onSiteEdit }: Props) => {
<div className='d-flex justify-content-between align-items-center card-title'>
<Link className='nav-link flex-grow-1 me-3' to={appRoutes.site(site.id)}>
<div className='d-flex gap-1 align-items-center flex-grow-1'>
<PrimaryIconBadge type='school' />
<IconBadge iconKey={getSiteTypeIconKey(site.siteType.id)} />
<h5 className='mb-0 text-ellipsis'>{site.name}</h5>
</div>
</Link>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useTranslation } from 'react-i18next'

import { type Coordinate, defaultLatitude, defaultLongitude } from '@models/types/Coordinate'
import { type Coordinate, defaultLatitude, defaultLongitude } from '@models/Coordinate'

type Props = {
readonly latitude?: Coordinate,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import SiteCoordinates from '@components/analytics/site-modal/SiteCoordinates'
import TreeSpeciesSelector from '@components/analytics/TreeSpeciesSelector'
import { LanguageContext } from '@components/context/LanguageContext'
import useApiClient from '@hooks/ApiClientHook'
import { type Coordinate, defaultLatitude, defaultLongitude, extractCoordinate } from '@models/types/Coordinate'
import { type Coordinate, defaultLatitude, defaultLongitude, extractCoordinate } from '@models/Coordinate'
import { type SiteType, Species } from '@services/api'
import { getApiBaseUrl } from '@services/apiSettings'

Expand Down
21 changes: 21 additions & 0 deletions canopeum_frontend/src/components/assets/SiteTypePin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import CanopeumPin from '@assets/icons/pins/canopeum-pin.svg'
import CorporateLotPin from '@assets/icons/pins/corporate-lot-pin.svg'
import EducationalFacilityPin from '@assets/icons/pins/educational-facility-pin.svg'
import FarmsLandPin from '@assets/icons/pins/farms-land-pin.svg'
import IndegeniousCommunityPin from '@assets/icons/pins/indegenious-community-pin.svg'
import ParkPin from '@assets/icons/pins/park-pin.svg'
import type { SiteTypeID } from '@models/SiteType'

const pinMap: Record<SiteTypeID, JSX.Element> = {
1: <img alt='CanopeumPin' src={CanopeumPin} />,
2: <img alt='ParkPin' src={ParkPin} />,
3: <img alt='IndegeniousCommunityPin' src={IndegeniousCommunityPin} />,
4: <img alt='EducationalFacilityPin' src={EducationalFacilityPin} />,
5: <img alt='FarmsLandPin' src={FarmsLandPin} />,
6: <img alt='CorporateLotPin' src={CorporateLotPin} />,
}

type Props = { siteTypeId: keyof typeof pinMap }

const SiteTypePin = ({ siteTypeId }: Props) => pinMap[siteTypeId]
export default SiteTypePin
8 changes: 7 additions & 1 deletion canopeum_frontend/src/components/settings/AdminCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ const AdminCard = ({ admin }: Props) => (
<div className='card-text mt-3 d-flex flex-column gap-1'>
{admin.sites.map(site => (
<div className='d-flex align-items-center text-primary' key={site.id}>
<span className='material-symbols-outlined'>school</span>
{
/* See TODO
<span className='material-symbols-outlined'>
{getSiteTypeIconKey(site.siteType.id)}
</span> */
}
(TODO: Add site icon here)
<span className='ms-1'>{site.name}</span>
</div>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useState } from 'react'
import { Link } from 'react-router-dom'

import SiteAnnouncementModal from '@components/social/site-modal/SiteAnnouncementModal'
import type { PageViewMode } from '@models/types/PageViewMode.Type'
import type { PageViewMode } from '@models/PageViewMode.type'
import type { Announcement } from '@services/api'

type Props = {
Expand Down
2 changes: 1 addition & 1 deletion canopeum_frontend/src/components/social/ContactCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import instagramLogo from '@assets/icons/instagram-contact-logo.svg'
import linkedinLogo from '@assets/icons/linkedin-contact-logo.svg'
import xLogo from '@assets/icons/x-contact-logo.svg'
import SiteContactModal from '@components/social/site-modal/SiteContactModal'
import type { PageViewMode } from '@models/types/PageViewMode.Type'
import type { PageViewMode } from '@models/PageViewMode.type'
import type { Contact } from '@services/api'

type Props = {
Expand Down
2 changes: 1 addition & 1 deletion canopeum_frontend/src/components/social/PostCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import TextExpansion from '@components/inputs/TextExpansion'
import PostCommentsDialog from '@components/social/PostCommentsDialog'
import SharePostDialog from '@components/social/SharePostDialog'
import useApiClient from '@hooks/ApiClientHook'
import type { PageViewMode } from '@models/types/PageViewMode.Type'
import type { PageViewMode } from '@models/PageViewMode.type'
import type { Post } from '@services/api'
import usePostsStore from '@store/postsStore'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import { useTranslation } from 'react-i18next'
import BatchSponsorLogo from '@components/batches/BatchSponsorLogo'
import { AuthenticationContext } from '@components/context/AuthenticationContext'
import { LanguageContext } from '@components/context/LanguageContext'
import IconBadge from '@components/IconBadge'
import ToggleSwitch from '@components/inputs/ToggleSwitch'
import PrimaryIconBadge from '@components/PrimaryIconBadge'
import useApiClient from '@hooks/ApiClientHook'
import type { PageViewMode } from '@models/types/PageViewMode.Type'
import type { PageViewMode } from '@models/PageViewMode.type'
import { getSiteTypeIconKey } from '@models/SiteType'
import { PatchedUpdateSitePublicStatus, type SiteSocial, User } from '@services/api'
import { getApiBaseUrl } from '@services/apiSettings'

Expand Down Expand Up @@ -120,7 +121,7 @@ const SiteSocialHeader = ({ site, viewMode }: Props) => {
</div>

<div className='card-text d-flex flex-row align-items-center gap-1'>
<PrimaryIconBadge type='school' />
<IconBadge iconKey={getSiteTypeIconKey(site.siteType.id)} />
<h4 className='fw-bold text-primary mb-0'>{translateValue(site.siteType)}</h4>
</div>

Expand Down
24 changes: 24 additions & 0 deletions canopeum_frontend/src/models/SiteType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// This mapping MUST MATCH site_type_names in
// canopeum_backend/canopeum_backend/management/commands/initialize_database.py
const SITE_TYPE_ID_TO_ICON_KEY = {
1: 'eco', // Canopeum // TODO: Update to proper icon
2: 'forest', // Parks
3: 'workspaces', // Indigenous community
4: 'school', // Educational Facility
5: 'psychiatry', // Farms Land // TODO: Update to proper icon
6: 'source_environment', // Corporate Lot
} as const
export const getSiteTypeIconKey = (siteTypeId: number): SiteTypeIconKey => {
const iconKey = SITE_TYPE_ID_TO_ICON_KEY[siteTypeId as SiteTypeID]
/* eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
-- Additional runtime safety */
if (!iconKey) {
throw new RangeError(`${siteTypeId} is not a known site type ID`)
}

return iconKey
}
export type SiteTypeID = keyof typeof SITE_TYPE_ID_TO_ICON_KEY
export type SiteTypeIconKey = typeof SITE_TYPE_ID_TO_ICON_KEY[SiteTypeID]
export const SITE_TYPE_ICON_KEYS = Object.values(SITE_TYPE_ID_TO_ICON_KEY)
export const SITE_TYPE_IDS = Object.keys(SITE_TYPE_ID_TO_ICON_KEY).map(Number) as SiteTypeID[]
2 changes: 1 addition & 1 deletion canopeum_frontend/src/pages/Analytics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { AuthenticationContext } from '@components/context/AuthenticationContext
import { LanguageContext } from '@components/context/LanguageContext'
import { SnackbarContext } from '@components/context/SnackbarContext'
import useApiClient from '@hooks/ApiClientHook'
import { coordinateToString } from '@models/types/Coordinate'
import { coordinateToString } from '@models/Coordinate'
import type { SiteSummary, User } from '@services/api'
import { assetFormatter } from '@utils/assetFormatter'

Expand Down
23 changes: 6 additions & 17 deletions canopeum_frontend/src/pages/Map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,13 @@ import { useCallback, useEffect, useState } from 'react'
import ReactMap, { GeolocateControl, Marker, NavigationControl, ScaleControl, type ViewState } from 'react-map-gl/maplibre'
import { Link } from 'react-router-dom'

import CanopeumPin from '@assets/icons/pins/canopeum-pin.svg'
import CorporateLotPin from '@assets/icons/pins/corporate-lot-pin.svg'
import EducationalFacilityPin from '@assets/icons/pins/educational-facility-pin.svg'
import FarmsLandPin from '@assets/icons/pins/farms-land-pin.svg'
import IndegeniousCommunityPin from '@assets/icons/pins/indegenious-community-pin.svg'
import ParkPin from '@assets/icons/pins/park-pin.svg'
import SiteTypePin from '@components/assets/SiteTypePin'
import { appRoutes } from '@constants/routes.constant'
import useApiClient from '@hooks/ApiClientHook'
import { getSiteTypeIconKey, type SiteTypeID } from '@models/SiteType'
import type { SiteMap } from '@services/api'
import { getApiBaseUrl } from '@services/apiSettings'

const pinMap: Record<number, string> = {
1: CanopeumPin,
2: ParkPin,
3: IndegeniousCommunityPin,
4: EducationalFacilityPin,
5: FarmsLandPin,
6: CorporateLotPin,
}

type MarkerEvent = {
target: {
_lngLat: {
Expand Down Expand Up @@ -100,7 +87,7 @@ const Map = () => {
onClick={event => onMarkerClick(event, site)}
style={{ cursor: 'pointer' }}
>
<img alt='' src={pinMap[site.siteType.id]} />
<SiteTypePin siteTypeId={site.siteType.id as SiteTypeID} />
</Marker>
))}
</ReactMap>
Expand Down Expand Up @@ -133,7 +120,9 @@ const Map = () => {
<h5>{site.name}</h5>

<h6 className='d-flex align-items-center text-primary'>
<span className='material-symbols-outlined'>school</span>
<span className='material-symbols-outlined'>
{getSiteTypeIconKey(site.siteType.id)}
</span>
<span className='ms-1'>{site.siteType.en}</span>
</h6>

Expand Down
2 changes: 1 addition & 1 deletion canopeum_frontend/src/pages/SiteSocialPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import WidgetCard from '@components/social/WidgetCard'
import WidgetDialog from '@components/social/WidgetDialog'
import useApiClient from '@hooks/ApiClientHook'
import usePostsInfiniteScrolling from '@hooks/PostsInfiniteScrollingHook'
import type { PageViewMode } from '@models/types/PageViewMode.Type'
import type { PageViewMode } from '@models/PageViewMode.type'
import { type IWidget, PatchedWidget, type Post, type SiteSocial, Widget } from '@services/api'
import { ensureError } from '@services/errors'
import usePostsStore from '@store/postsStore'
Expand Down
39 changes: 21 additions & 18 deletions canopeum_frontend/src/pages/Utilities.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

import facebookLogo from '@assets/icons/facebook-regular.svg'
import canopeumLogo from '@assets/images/Canopeum_Logo.jpg'
import PrimaryIconBadge from '@components/PrimaryIconBadge'
import IconBadge from '@components/IconBadge'
import { getSiteTypeIconKey, SITE_TYPE_IDS } from '@models/SiteType'

const Utilities = () => (
<div>
Expand All @@ -19,9 +20,7 @@ const Utilities = () => (
<span className='material-symbols-outlined icon-xl'>pin_drop</span>
<span className='material-symbols-outlined fill-icon icon-2xl'>account_circle</span>
<span className='material-symbols-outlined icon-3xl'>account_circle</span>
<span className='material-symbols-outlined fill-icon icon-4xl'>eco</span>
<span className='material-symbols-outlined'>eco</span>
<span className='material-symbols-outlined fill-icon'>sms</span>
<span className='material-symbols-outlined fill-icon icon-4xl'>sms</span>
<span className='material-symbols-outlined'>sms</span>
<span className='material-symbols-outlined fill-icon'>mood</span>
<span className='material-symbols-outlined'>mood</span>
Expand All @@ -41,29 +40,33 @@ const Utilities = () => (
<span className='material-symbols-outlined'>add</span>
<span className='material-symbols-outlined fill-icon'>cancel</span>
<span className='material-symbols-outlined'>cancel</span>
<span className='material-symbols-outlined fill-icon'>source_environment</span>
<span className='material-symbols-outlined'>source_environment</span>
<span className='material-symbols-outlined fill-icon'>location_on</span>
<span className='material-symbols-outlined'>location_on</span>
<span className='material-symbols-outlined fill-icon'>person</span>
<span className='material-symbols-outlined'>person</span>
<span className='material-symbols-outlined fill-icon'>forest</span>
<span className='material-symbols-outlined'>forest</span>
<span className='material-symbols-outlined fill-icon'>workspaces</span>
<span className='material-symbols-outlined'>workspaces</span>
<span className='material-symbols-outlined fill-icon'>school</span>
<span className='material-symbols-outlined'>school</span>
<span className='material-symbols-outlined fill-icon'>psychiatry</span>
<span className='material-symbols-outlined'>psychiatry</span>
<img alt='iconHome' className='h-1' src={facebookLogo} />

<h4>Site Type Icons</h4>
{SITE_TYPE_IDS.map(siteTypeId => {
const key = getSiteTypeIconKey(siteTypeId)

return (
<>
<span className='material-symbols-outlined fill-icon' key={`${key}-fill`}>{key}</span>
<span className='material-symbols-outlined' key={key}>{key}</span>
<span className='material-symbols-outlined icon-2xl' key={`${key}-2xl`}>{key}</span>
</>
)
})}
</div>
<div className='bg-cream rounded-2 px-3 py-2'>
<h2>Badges</h2>
<div className='d-flex gap-1'>
<PrimaryIconBadge type='school' />
<PrimaryIconBadge type='forest' />
<PrimaryIconBadge type='workspaces' />
<PrimaryIconBadge type='person' />
{SITE_TYPE_IDS.map(siteTypeId => {
const key = getSiteTypeIconKey(siteTypeId)

return <IconBadge iconKey={key} key={key} />
})}
</div>
</div>
<div className='bg-cream rounded px-3 py-2'>
Expand Down
Loading