Skip to content
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
21 changes: 21 additions & 0 deletions backend/modules/upload/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ const UploadHelper = {
generateEventTag: slug => {
return `${cloudinaryRootPath}-event-${slug}`
},
generateChallengeTag: slug => {
return `${cloudinaryRootPath}-challenge-${slug}`
},
generateUserTag: userId => {
return `${cloudinaryRootPath}-user-${userId}`
},
Expand Down Expand Up @@ -129,6 +132,24 @@ const UploadHelper = {
}).single('image')
},

uploadChallengeLogo: slug => {
const storage = createStorageWithPath(
`challenge/logos/`,
{
width: 640,
height: 640,
crop: 'fit',
},
{
tag: UploadHelper.generateChallengeTag(slug),
},
)
return multer({
storage,
limits: { fileSize: 2 * 1024 * 1024 },
}).single('image')
},

uploadTravelGrantReceipt: (slug, userId) => {
const storage = createDocumentStorageWithPath(
`events/travel-grant-receipts/`,
Expand Down
26 changes: 26 additions & 0 deletions backend/modules/upload/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,32 @@ router.post(
},
)

/**
* Upload a logo for a challenge
*/
router.post(
'/challenges/:slug/logo',
hasToken,
hasPermission(Auth.Permissions.MANAGE_EVENT),
isEventOrganiser,
(req, res, next) => {
helper.uploadChallengeLogo(req.params.slug)(req, res, function (err) {
if (err) {
if (err.code === 'LIMIT_FILE_SIZE') {
next(new ForbiddenError(err.message))
} else {
next(err)
}
} else {
res.status(200).json({
url: req.file.secure_url || req.file.url,
publicId: req.file.public_id,
})
}
})
},
)

/**
* Upload icon for a hackerpack partner
*/
Expand Down
42 changes: 42 additions & 0 deletions frontend/src/components/challenges/ChallengeDetail.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react'
import { Box, Divider } from '@material-ui/core'

import GradientBox from 'components/generic/GradientBox'
import ChallengeSection from './ChallengeSection'

const makeBoxStyles = focus => ({
boxShadow: `2px 7px 30px rgb(0 0 0 / ${focus ? '12' : '4'}%)`,
cursor: 'pointer',
})

const ChallengeDetail = ({
partner,
title,
subtitle,
logo,
link,
isFocused,
}) => {
return (
<>
<Box p={2}>
<GradientBox
style={makeBoxStyles(isFocused)}
color={'theme_white'}
p={3}
>
<ChallengeSection
partner={partner}
title={title}
subtitle={subtitle}
logo={logo}
link={link}
/>
</GradientBox>
</Box>

<Divider variant="middle" />
</>
)
}
export default ChallengeDetail
58 changes: 58 additions & 0 deletions frontend/src/components/challenges/ChallengeSection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React from 'react'
import { makeStyles } from '@material-ui/core/styles'
import { Box, Typography } from '@material-ui/core'

import Markdown from 'components/generic/Markdown'

import { OutboundLink } from 'react-ga'

const useStyles = makeStyles(theme => ({
companyLogo: {
width: '200px',
},
outboundLink: {
'& a': {
textDecoration: 'none !important',
},
},
wrapper: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
[theme.breakpoints.up('md')]: {
flexDirection: 'row',
alignItems: 'flex-start',
},
},
}))

export default ({ partner, title, subtitle, logo, link }) => {
const classes = useStyles()

return (
<Box className={classes.wrapper}>
<Box p={3}>
{logo && (
<img
alt={partner}
src={logo.url}
className={classes.companyLogo}
/>
)}
</Box>
<Box p={3}>
<Typography variant="h5">{title}</Typography>
<Typography>
<Markdown source={subtitle} />
</Typography>
<Box className={classes.outboundLink}>
<OutboundLink
eventLabel="myLabel"
to={link}
target="_blank"
></OutboundLink>
</Box>
</Box>
</Box>
)
}
12 changes: 12 additions & 0 deletions frontend/src/components/inputs/BottomBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import ErrorOutlineIcon from '@material-ui/icons/ErrorOutline'

import Button from 'components/generic/Button'
import BlockExitIfDirty from 'components/inputs/BlockExitIfDirty/index'
import { isArray } from 'lodash-es'

const useStyles = makeStyles(theme => ({
wrapper: ({ dirty, hasErrors }) => ({
Expand Down Expand Up @@ -120,6 +121,17 @@ const BottomBar = ({ errors, dirty, onSubmit, loading }) => {
<ListItemText primary={errorMsg} />
</ListItem>
)
// TODO: improve
} else if (isArray(errorMsg)) {
if (showErrors)
console.info(`${field} errors`, errorMsg)
return (
<ListItem divider key={field}>
<ListItemText
primary={`Multiple errors in ${field}`}
/>
</ListItem>
)
} else {
return Object.keys(errorMsg).map(key => (
<ListItem divider key={key}>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/_dashboard/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export default () => {
<Switch>
<Route
exact={false}
path={`${match.url}/:slug`}
path={`${match.path}/:slug`}
component={SlugPage}
/>

Expand Down
110 changes: 110 additions & 0 deletions frontend/src/pages/_dashboard/slug/challenges/ChallengePage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { Box, makeStyles } from '@material-ui/core'
import { ArrowBack } from '@material-ui/icons'
import Button from 'components/generic/Button'
import Markdown from 'components/generic/Markdown'
import PageHeader from 'components/generic/PageHeader'
import React from 'react'

const useStyles = makeStyles(theme => ({
companyLogo: {
width: '200px',
marginRight: '24px',
},
outboundLink: {
'& a': {
textDecoration: 'none !important',
},
},
wrapper: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
[theme.breakpoints.up('md')]: {
flexDirection: 'row',
alignItems: 'flex-start',
},
},
subtitle: {
color: theme.palette.theme_orange.main,
},
}))

export default ({
onClose,
description,
title,
partner,
insights,
resources,
prizes,
criteria,
companyInfo,
logo,
}) => {
const classes = useStyles()

return (
<>
<Button
variant="contained"
color="theme_turquoise"
startIcon={<ArrowBack />}
onClick={onClose}
>
Back to all Challenges
</Button>
<Box className={classes.wrapper} pt={8} pb={3}>
{logo && (
<img
alt={partner}
src={logo.url}
className={classes.companyLogo}
/>
)}
<PageHeader heading={title} />
</Box>

{description && (
<>
<h1 className={classes.subtitle}>The challenge</h1>
<Markdown source={description} />
</>
)}
<br />
{insights && (
<>
<h1 className={classes.subtitle}>Insights</h1>
<Markdown source={insights} />
</>
)}
<br />
{resources && (
<>
<h1 className={classes.subtitle}>What we'll bring</h1>
<Markdown source={resources} />
</>
)}
<br />
{prizes && (
<>
<h1 className={classes.subtitle}>The Prizes</h1>
<Markdown source={prizes} />
</>
)}
<br />
{criteria && (
<>
<h1 className={classes.subtitle}>Judging criteria</h1>
<Markdown source={criteria} />
</>
)}
<br />
{companyInfo && (
<>
<h1 className={classes.subtitle}>About the company</h1>
<Markdown source={companyInfo} />
</>
)}
</>
)
}
60 changes: 60 additions & 0 deletions frontend/src/pages/_dashboard/slug/challenges/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React, { useState, useEffect } from 'react'
import { Box, makeStyles, Typography } from '@material-ui/core'
import { Helmet } from 'react-helmet'
import PageHeader from 'components/generic/PageHeader'
import PageWrapper from 'components/layouts/PageWrapper'
import * as DashboardSelectors from 'redux/dashboard/selectors'

import ChallengeDetail from 'components/challenges/ChallengeDetail'
import { useSelector } from 'react-redux'
import ChallengePage from './ChallengePage'
export default () => {
const event = useSelector(DashboardSelectors.event)
const [openChallenge, setOpenChallenge] = useState(null)
const [activeChallenge, setActiveChallenge] = useState(null)

const challenges = event.challenges
console.info('challenges', challenges)
console.info('open challenge', openChallenge)
if (openChallenge !== null) {
console.info('here')
return (
<ChallengePage
onClose={() => setOpenChallenge(null)}
{...challenges[openChallenge]}
/>
)
}
return (
<>
<Helmet>
<title>Junction App || Dashboard</title>
</Helmet>
<PageHeader
heading="Challenges"
subheading="Get to know the exciting Challenges and the wonderful Partners providing them!"
/>
<PageWrapper loading={false}>
{challenges.map((c, index) => (
<div
onClick={() => setOpenChallenge(index)}
onMouseOver={() => setActiveChallenge(index)}
onMouseLeave={() => setActiveChallenge(null)}
>
<ChallengeDetail
isFocused={activeChallenge === index}
{...c}
/>
</div>
))}
<Box p={2}>
<Typography color="textSecondary" variant="subtitle1">
Anything you would like to see here in the future?
Contact us at partnerships@hackjunction.com with your
suggestion.
</Typography>
</Box>
</PageWrapper>
</>
)
}
Loading