Skip to content

Commit

Permalink
Merge pull request #263 from developmentseed/enhance/edit-badge-assig…
Browse files Browse the repository at this point in the history
…nment
  • Loading branch information
vgeorge authored Apr 12, 2022
2 parents 759c366 + 0636c98 commit 0051b7b
Show file tree
Hide file tree
Showing 8 changed files with 267 additions and 42 deletions.
22 changes: 13 additions & 9 deletions app/manage/badges.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,8 @@ const assignUserBadge = routeWrapper({
})
.required(),
body: yup.object({
assigned_at: yup.date().optional(),
valid_until: yup.date().optional()
assigned_at: yup.date().required(),
valid_until: yup.date().nullable()
})
},
handler: async function (req, reply) {
Expand All @@ -218,12 +218,13 @@ const assignUserBadge = routeWrapper({
}

// assign badge
const { assigned_at, valid_until } = req.body
const [badge] = await conn('user_badges')
.insert({
user_id: req.params.userId,
badge_id: req.params.badgeId,
assigned_at: req.body.assigned_at,
valid_until: req.body.valid_until
assigned_at: assigned_at.toISOString(),
valid_until: valid_until ? valid_until.toISOString() : null
})
.returning('*')

Expand Down Expand Up @@ -275,19 +276,22 @@ const updateUserBadge = routeWrapper({
})
.required(),
body: yup.object({
assigned_at: yup.date().optional(),
valid_until: yup.date().optional()
assigned_at: yup.date().required(),
valid_until: yup.date().nullable()
})
},
handler: async function (req, reply) {
try {
const conn = await db()

// assign badge
const { assigned_at, valid_until } = req.body

// Yup validation returns time-zoned dates, update query use UTC strings
// to avoid that.
const [badge] = await conn('user_badges')
.update({
assigned_at: req.body.assigned_at,
valid_until: req.body.valid_until
assigned_at: assigned_at.toISOString(),
valid_until: valid_until ? valid_until.toISOString() : null
})
.where({
user_id: req.params.userId,
Expand Down
7 changes: 7 additions & 0 deletions app/manage/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,13 @@ function manageRouter (nextApp) {
})
}
)
router.get(
'/organizations/:id/badges/:badgeId/assign/:userId',
can('organization:edit'),
(req, res) => {
return nextApp.render(req, res, '/badges/assign', req.params)
}
)

return router
}
Expand Down
2 changes: 2 additions & 0 deletions app/tests/api/badges-api.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -366,13 +366,15 @@ test('Update badge', async (t) => {
const badgeAssignment = (await orgOwner.agent
.patch(updateBadgeRoute)
.send({
assigned_at: '2020-01-01Z',
valid_until: '2021-01-01Z'
})
.expect(200)).body

t.like(badgeAssignment, {
badge_id: badge2.id,
user_id: orgTeamMember.id,
assigned_at: '2020-01-01T00:00:00.000Z',
valid_until: '2021-01-01T00:00:00.000Z'
})
})
Expand Down
2 changes: 1 addition & 1 deletion components/button.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export default function Button ({ name, id, value, variant, type, disabled, href
if (href) {
let fullUrl
(href.startsWith('http')) ? (fullUrl = href) : (fullUrl = join(publicRuntimeConfig.APP_URL, href))
return <a href={fullUrl} className={[`button`, variant, size].join(' ')} disabled={disabled} name={name} id={id}>{children}<style jsx>{style}</style></a>
return <a href={fullUrl} className={[`button`, variant, size].join(' ')} disabled={disabled} name={name} id={id}>{children || value}<style jsx>{style}</style></a>
}
return <div onClick={onClick} className={[`button`, variant, size].join(' ')} disabled={disabled}>{children}<style jsx>{style}</style></div>
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"connect-session-knex": "^2.1.1",
"cors": "^2.8.5",
"csurf": "^1.11.0",
"date-fns": "^2.28.0",
"dotenv": "^6.2.0",
"dotenv-webpack": "^1.8.0",
"express": "^4.17.2",
Expand Down
171 changes: 171 additions & 0 deletions pages/badges/assign.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import React, { Component } from 'react'
import { Formik, Field, Form } from 'formik'
import APIClient from '../../lib/api-client'
import Button from '../../components/button'
import { format } from 'date-fns'
import { toast } from 'react-toastify'

const apiClient = new APIClient()

function ButtonWrapper ({ children }) {
return (
<div>
{children}
<style jsx global>{`
.button {
margin-right: 10px;
}
}`}</style>
</div>
)
}

export default class AssignBadge extends Component {
static async getInitialProps ({ query }) {
if (query) {
return {
orgId: query.id,
badgeId: query.badgeId,
userId: query.userId
}
}
}

constructor (props) {
super(props)
this.state = {}

this.loadData = this.loadData.bind(this)
}

async componentDidMount () {
this.loadData()
}

async loadData () {
const { orgId, badgeId, userId } = this.props
try {
const [org, badge] = await Promise.all([
apiClient.get(`/organizations/${orgId}`),
apiClient.get(`/organizations/${orgId}/badges/${badgeId}`)
])

// Check if user already has the badge
const user =
badge.users && badge.users.find((u) => u.id === parseInt(userId))

this.setState({
org,
badge,
user
})
} catch (error) {
console.error(error)
this.setState({
error,
loading: false
})
}
}

renderPageInner () {
if (this.state.error) {
return <div>An unexpected error occurred, please try again later.</div>
}

if (!this.state.org && !this.state.badge) {
return <div>Loading...</div>
}

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

return (
<>
<div className='page__heading'>
<h1>{badge.name} Badge</h1>
</div>
<section>
<div className='page__heading'>
<h2>User: {userId} (OSM id)</h2>
</div>
<Formik
initialValues={{
assignedAt:
(user && user.assignedAt && user.assignedAt.substring(0, 10)) ||
format(Date.now(), 'yyyy-MM-dd'),
validUntil:
(user && user.validUntil && user.validUntil.substring(0, 10)) ||
''
}}
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
)
toast.info('Badge assigned successfully.')
} else {
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, errors }) => {
return (
<Form>
<div className='form-control form-control__vertical'>
<label htmlFor='assignedAt'>Assigned At (required)</label>
<Field
name='assignedAt'
type='date'
value={values.assignedAt}
/>
</div>
<div className='form-control form-control__vertical'>
<label htmlFor='validUntil'>Valid Until</label>
<Field
name='validUntil'
type='date'
value={values.validUntil}
/>
</div>
<ButtonWrapper>
<Button
disabled={isSubmitting}
variant='primary'
type='submit'
value={user ? 'Update' : 'Assign'}
/>
<Button
variant='small'
href={`/organizations/${orgId}/badges/${badgeId}`}
value='Go to badge view'
/>
</ButtonWrapper>
</Form>
)
}}
/>
</section>
</>
)
}

render () {
return <article className='inner page'>{this.renderPageInner()}</article>
}
}
Loading

0 comments on commit 0051b7b

Please sign in to comment.