diff --git a/app/index.js b/app/index.js
index 0a1bfdb3..2afaf13c 100644
--- a/app/index.js
+++ b/app/index.js
@@ -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')
diff --git a/app/manage/index.js b/app/manage/index.js
index a44ca0be..4fd7b603 100644
--- a/app/manage/index.js
+++ b/app/manage/index.js
@@ -302,18 +302,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
diff --git a/components/profile-modal.js b/components/profile-modal.js
index bb83481d..18ae1c02 100644
--- a/components/profile-modal.js
+++ b/components/profile-modal.js
@@ -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 (
@@ -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 (
+
+ {badges.map((b) => (
+
+
+
+ |
+ {b.name} |
+
+ ))}
+
+ )
+}
+
+export default function ProfileModal ({
+ user,
+ attributes,
+ badges,
+ onClose,
+ actions
+}) {
actions = actions || []
let profileContent = User does not have a profile
if (!isEmpty(attributes)) {
@@ -87,13 +113,18 @@ export default function ProfileModal ({ user, attributes, onClose, actions }) {
`}
>
}
- return
- { user.img ? : '' }
-
- {user.name}
- {!isEmpty(actions) && renderActions(actions)}
-
- {profileContent}
-
-
+ return (
+
+ {user.img ? : ''}
+
+ {user.name}
+ {!isEmpty(actions) && renderActions(actions)}
+
+ {profileContent}
+ {renderBadges(badges)}
+
+
+ )
}
diff --git a/components/svg-square.js b/components/svg-square.js
new file mode 100644
index 00000000..0c33d9bc
--- /dev/null
+++ b/components/svg-square.js
@@ -0,0 +1,9 @@
+import React from 'react'
+
+export default function SvgSquare ({ color, size = 20 }) {
+ return (
+
+ )
+}
diff --git a/pages/badges/assign.js b/pages/badges-assignment/edit.js
similarity index 72%
rename from pages/badges/assign.js
rename to pages/badges-assignment/edit.js
index 9298cb7b..f78c4a39 100644
--- a/pages/badges/assign.js
+++ b/pages/badges-assignment/edit.js
@@ -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'
@@ -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)
}
}
}
@@ -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)
@@ -99,8 +104,8 @@ export default class AssignBadge extends Component {
return Loading...
}
- const { orgId, userId } = this.props
- const { badges, badge, user } = this.state
+ const { orgId, userId, badgeId } = this.props
+ const { badge, assignment } = this.state
return (
<>
@@ -111,66 +116,57 @@ export default class AssignBadge extends Component {
{
+ 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 (