Skip to content

Commit

Permalink
Merge pull request #269 from developmentseed/enhance/display-profile-…
Browse files Browse the repository at this point in the history
…badges

Add badges to profile modal and other improvements
  • Loading branch information
vgeorge authored Apr 29, 2022
2 parents a35cc1d + b7cf630 commit 1406af4
Show file tree
Hide file tree
Showing 8 changed files with 365 additions and 133 deletions.
3 changes: 3 additions & 0 deletions app/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Set server timezone to UTC to avoid issues with date parsing
process.env.TZ = 'UTC'

const path = require('path')
const express = require('express')
const bodyParser = require('body-parser')
Expand Down
14 changes: 7 additions & 7 deletions app/manage/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -328,18 +328,18 @@ function manageRouter (nextApp) {
}
)

// Use same page for two routes
const assignBadgePageRoute = [
can('organization:edit'),
(req, res) => nextApp.render(req, res, '/badges/assign', req.params)
]
// New badge assignment
router.get(
'/organizations/:id/badges/assign/:userId',
...assignBadgePageRoute
can('organization:edit'),
(req, res) => nextApp.render(req, res, '/badges-assignment/new', req.params)
)

// Edit badge assignment
router.get(
'/organizations/:id/badges/:badgeId/assign/:userId',
...assignBadgePageRoute
can('organization:edit'),
(req, res) => nextApp.render(req, res, '/badges-assignment/edit', req.params)
)

return router
Expand Down
51 changes: 41 additions & 10 deletions components/profile-modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { isEmpty } from 'ramda'
import theme from '../styles/theme'
import Popup from 'reactjs-popup'
import Button from './button'
import SvgSquare from '../components/svg-square'

function renderActions (actions) {
return (
Expand Down Expand Up @@ -43,7 +44,32 @@ function renderActions (actions) {
)
}

export default function ProfileModal ({ user, attributes, onClose, actions }) {
function renderBadges (badges) {
if (!badges || badges.length === 0) {
return null
}

return (
<table>
{badges.map((b) => (
<tr>
<td>
<SvgSquare color={b.color} />
</td>
<td>{b.name}</td>
</tr>
))}
</table>
)
}

export default function ProfileModal ({
user,
attributes,
badges,
onClose,
actions
}) {
actions = actions || []
let profileContent = <dl>User does not have a profile</dl>
if (!isEmpty(attributes)) {
Expand Down Expand Up @@ -87,13 +113,18 @@ export default function ProfileModal ({ user, attributes, onClose, actions }) {
`}</style>
</>
}
return <article className='modal__details'>
{ user.img ? <img src={user.img} /> : '' }
<h2 style={{ display: 'flex', 'justifyContent': 'space-between' }}>
<span>{user.name}</span>
{!isEmpty(actions) && renderActions(actions)}
</h2>
{profileContent}
<Button size='small' onClick={() => onClose()}>close</Button>
</article>
return (
<article className='modal__details'>
{user.img ? <img src={user.img} /> : ''}
<h2 style={{ display: 'flex', justifyContent: 'space-between' }}>
<span>{user.name}</span>
{!isEmpty(actions) && renderActions(actions)}
</h2>
{profileContent}
{renderBadges(badges)}
<Button size='small' onClick={() => onClose()}>
close
</Button>
</article>
)
}
9 changes: 9 additions & 0 deletions components/svg-square.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react'

export default function SvgSquare ({ color, size = 20 }) {
return (
<svg width={size} height={size}>
<rect width={size} height={size} style={{ fill: color }} />
</svg>
)
}
104 changes: 53 additions & 51 deletions pages/badges/assign.js → pages/badges-assignment/edit.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { Component } from 'react'
import * as Yup from 'yup'
import { Formik, Field, Form } from 'formik'
import APIClient from '../../lib/api-client'
import Button from '../../components/button'
Expand Down Expand Up @@ -39,13 +40,13 @@ function Section ({ children }) {
)
}

export default class AssignBadge extends Component {
export default class EditBadgeAssignment extends Component {
static async getInitialProps ({ query }) {
if (query) {
return {
orgId: query.id,
badgeId: query.badgeId,
userId: query.userId
badgeId: parseInt(query.badgeId),
userId: parseInt(query.userId)
}
}
}
Expand All @@ -64,22 +65,26 @@ export default class AssignBadge extends Component {
}

async loadData () {
const { orgId, badgeId } = this.props
const { orgId, badgeId, userId } = this.props

try {
const org = await apiClient.get(`/organizations/${orgId}`)
let badge, badges
const badge = await apiClient.get(
`/organizations/${orgId}/badges/${badgeId}`
)
let assignment
if (badge && badge.users) {
assignment = badge.users.find((u) => u.id === parseInt(userId))
}

if (badgeId) {
badge = await apiClient.get(`/organizations/${orgId}/badges/${badgeId}`)
} else {
badges = await apiClient.get(`/organizations/${orgId}/badges`)
if (!assignment) {
throw Error('Badge assignment not found.')
}

this.setState({
org,
badge,
badges
assignment
})
} catch (error) {
console.error(error)
Expand All @@ -99,8 +104,8 @@ export default class AssignBadge extends Component {
return <div>Loading...</div>
}

const { orgId, userId } = this.props
const { badges, badge, user } = this.state
const { orgId, userId, badgeId } = this.props
const { badge, assignment } = this.state

return (
<>
Expand All @@ -111,73 +116,67 @@ export default class AssignBadge extends Component {
<Formik
initialValues={{
assignedAt:
(user && user.assignedAt && user.assignedAt.substring(0, 10)) ||
(assignment &&
assignment.assignedAt &&
assignment.assignedAt.substring(0, 10)) ||
format(Date.now(), 'yyyy-MM-dd'),
validUntil:
(user && user.validUntil && user.validUntil.substring(0, 10)) ||
(assignment &&
assignment.validUntil &&
assignment.validUntil.substring(0, 10)) ||
''
}}
onSubmit={async ({ assignedAt, validUntil, badgeId }) => {
validationSchema={Yup.object().shape({
assignedAt: Yup.date().required(
'Please select an assignment date.'
),
validUntil: Yup.date().when(
'assignedAt',
(assignedAt, schema) =>
assignedAt &&
schema.min(
assignedAt,
'End date must be after the start date.'
)
)
})}
onSubmit={async ({ assignedAt, validUntil }) => {
try {
const payload = {
assigned_at: assignedAt,
valid_until: validUntil !== '' ? validUntil : null
}

if (!user) {
await apiClient.post(
`/organizations/${orgId}/badges/${badgeId}/assign/${userId}`,
payload
)
Router.push(
join(
URL,
`/organizations/${orgId}/badges/${badgeId}/assign/${userId}`
)
)
} else {
await apiClient.patch(
`/organizations/${orgId}/member/${userId}/badge/${badgeId}`,
payload
)
toast.info('Badge updated successfully.')
}
await apiClient.patch(
`/organizations/${orgId}/member/${userId}/badge/${badgeId}`,
payload
)
toast.info('Badge updated successfully.')
this.loadData()
} catch (error) {
console.log(error)
toast.error(`Unexpected error, please try again later.`)
}
}}
render={({ isSubmitting, values }) => {
render={({ isSubmitting, values, errors }) => {
return (
<Form>
<div className='page__heading'>
<h2>User: {userId} (OSM id)</h2>
</div>
{badge ? (
<div className='page__heading'>
<h2>Badge: {badge && badge.name}</h2>
</div>
) : (
<div className='form-control form-control__vertical'>
<label htmlFor='badgeId'>Badge:</label>
<Field as='select' name='badgeId'>
<option value=''>Select a badge</option>
{badges.map((b) => (
<option key={b.id} value={b.id}>
{b.name}
</option>
))}
</Field>
</div>
)}
<div className='page__heading'>
<h2>Badge: {badge && badge.name}</h2>
</div>
<div className='form-control form-control__vertical'>
<label htmlFor='assignedAt'>Assigned At (required)</label>
<Field
name='assignedAt'
type='date'
value={values.assignedAt}
/>
{errors.assignedAt && (
<div className='form--error'>{errors.assignedAt}</div>
)}
</div>
<div className='form-control form-control__vertical'>
<label htmlFor='validUntil'>Valid Until</label>
Expand All @@ -186,6 +185,9 @@ export default class AssignBadge extends Component {
type='date'
value={values.validUntil}
/>
{errors.validUntil && (
<div className='form--error'>{errors.validUntil}</div>
)}
</div>
<ButtonWrapper>
<Button
Expand Down
Loading

0 comments on commit 1406af4

Please sign in to comment.