Skip to content

Commit

Permalink
Implement toggle switch
Browse files Browse the repository at this point in the history
Add missing translations
Add placeholders for actions
Add user role check for site summary card view mode
  • Loading branch information
sam9291 committed Mar 25, 2024
1 parent 0a7e9dd commit 80d9738
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 27 deletions.
26 changes: 26 additions & 0 deletions canopeum_frontend/src/components/inputs/ToggleSwitch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
type Props = {
readonly text: string,
readonly checked: boolean,
readonly onChange: (checked: boolean) => void,
readonly additionalClassNames?: string,
}

const ToggleSwitch = ({ text, checked, onChange, additionalClassNames }: Props) => (
<div className={`form-check form-switch ${additionalClassNames ?? ''}`}>
<input
checked={checked}
className={`form-check-input ${
checked
? 'text-bg-primary'
: ''
}`}
onChange={event =>
onChange(event.target.checked)}
role='switch'
type='checkbox'
/>
<label className='form-check-label'>{text}</label>
</div>
)

export default ToggleSwitch
83 changes: 58 additions & 25 deletions canopeum_frontend/src/components/site/SiteSummaryCard.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,73 @@
import headerLogo from '@assets/images/map/MARR4059.png'
import ToggleSwitch from '@components/inputs/ToggleSwitch'
import PrimaryIconBadge from '@components/PrimaryIconBadge'
import type { SiteSocial } from '@services/api'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'

type Props = {
readonly viewMode: 'admin' | 'user' | 'visitor',
readonly site: SiteSocial,
}

const SiteSummaryCard = ({ site }: Props) => (
<div className='card mb-3'>
<div className='row g-0'>
<div className='col-md-4'>
{/* TODO: replace img source when backend offers an image endpoint */}
<img alt='header logo' className='img-fluid' src={headerLogo} />
</div>
<div className='col-md-8'>
<div className='card-body'>
<h1 className='fw-bold card-title'>{site.name}</h1>
<div className='card-text d-flex flex-row gap-1'>
<PrimaryIconBadge type='school' />
<h4 className='fw-bold text-primary'>{site.type.en}</h4>
</div>
<p className='card-text'>{site.description ?? ''}</p>
<div className='container fw-bold'>
<div className='mb-2'>
<span className='material-symbols-outlined align-middle'>person</span>
<span>Sponsors:</span>
const onFollowClick = () => {
// TODO implement follow here when backend is available
}

const updateSiteIsPublic = async (_: boolean) => {
// TODO Implement site update when backend is ready
}
const SiteSummaryCard = ({ site, viewMode }: Props) => {
const { t } = useTranslation()
const [isPublic, setIsPublic] = useState(true)

useEffect(() => void updateSiteIsPublic(isPublic), [isPublic])

return (
<div className='card mb-3'>
<div className='row g-0'>
<div className='col-md-4'>
{/* TODO: replace img source when backend offers an image endpoint */}
<img alt='header logo' className='img-fluid' src={headerLogo} />
</div>
<div className='col-md-8'>
<div className='card-body'>
<div className='d-flex flex-row justify-content-between'>
<h1 className='fw-bold card-title'>{site.name}</h1>
{viewMode === 'user' && (
<button className='btn btn-secondary' onClick={onFollowClick} type='button'>
{t('mapSite.siteSummaryCard.follow')}
</button>
)}
{viewMode === 'admin' && (
<ToggleSwitch
additionalClassNames='fs-4'
checked={isPublic}
onChange={setIsPublic}
text={t('mapSite.siteSummaryCard.public')}
/>
)}
</div>
<div className='row'>
{site.sponsors.map(sponsorName => (
<div className='col-12 col-sm-6 col-md-4 col-lg-3 mb-3' key={sponsorName}>{sponsorName}</div>
))}
<div className='card-text d-flex flex-row gap-1'>
<PrimaryIconBadge type='school' />
<h4 className='fw-bold text-primary'>{site.type.en}</h4>
</div>
<p className='card-text'>{site.description ?? ''}</p>
<div className='container fw-bold'>
<div className='mb-2'>
<span className='material-symbols-outlined align-middle'>person</span>
<span>{t('mapSite.siteSummaryCard.sponsors')}:</span>
</div>
<div className='row'>
{site.sponsors.map(sponsorName => (
<div className='col-12 col-sm-6 col-md-4 col-lg-3 mb-3' key={sponsorName}>{sponsorName}</div>
))}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
)
)
}
export default SiteSummaryCard
2 changes: 2 additions & 0 deletions canopeum_frontend/src/locale/en/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import analyticsJSON from './analytics.json'
import homeJSON from './home.json'
import mapSiteJSON from './mapSite.json'

const enJSON = {
translation: {
home: { ...homeJSON },
analytics: { ...analyticsJSON },
mapSite: { ...mapSiteJSON },
},
}

Expand Down
7 changes: 7 additions & 0 deletions canopeum_frontend/src/locale/en/mapSite.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"siteSummaryCard": {
"follow": "Follow",
"sponsors": "Sponsors",
"public": "Public"
}
}
2 changes: 2 additions & 0 deletions canopeum_frontend/src/locale/fr/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import analyticsJSON from './analytics.json'
import homeJSON from './home.json'
import mapSiteJSON from './mapSite.json'

const frJSON = {
translation: {
home: { ...homeJSON },
analytics: { ...analyticsJSON },
mapSite: { ...mapSiteJSON },
},
}

Expand Down
7 changes: 7 additions & 0 deletions canopeum_frontend/src/locale/fr/mapSite.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"siteSummaryCard": {
"follow": "Suivre",
"sponsors": "Commanditaires",
"public": "Publique"
}
}
11 changes: 9 additions & 2 deletions canopeum_frontend/src/pages/MapSite.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import { AuthenticationContext, UserRole } from '@components/context/AuthenticationContext'
import SiteSummaryCard from '@components/site/SiteSummaryCard'
import type { SiteSocial } from '@services/api'
import api from '@services/apiInterface'
import { ensureError } from '@services/errors'
import { useEffect, useState } from 'react'
import { useContext, useEffect, useState } from 'react'
import { useParams } from 'react-router-dom'

const MapSite = () => {
const { siteId } = useParams()
const { currentUser } = useContext(AuthenticationContext)
const [isLoadingSite, setIsLoadingSite] = useState(true)
const [error, setError] = useState<Error | undefined>(undefined)
const [site, setSite] = useState<SiteSocial>()
const viewMode = currentUser
? currentUser.role === UserRole.RegularUser
? 'user'
: 'admin'
: 'visitor'

const fetchSiteData = async (parsedSiteId: number) => {
setIsLoadingSite(true)
Expand Down Expand Up @@ -41,7 +48,7 @@ const MapSite = () => {
<p>{error.message}</p>
</div>
)
: (site && <SiteSummaryCard site={site} />)}
: (site && <SiteSummaryCard site={site} viewMode={viewMode} />)}

<div className='container px-0'>
<div className='row'>
Expand Down

0 comments on commit 80d9738

Please sign in to comment.