Skip to content

Commit baa5ffe

Browse files
authored
Merge pull request #726 from samurotko/Junction-Platform-2.0
added venue map and edit team modal
2 parents 8a30343 + 06e0036 commit baa5ffe

24 files changed

Lines changed: 640 additions & 82 deletions

File tree

backend/modules/event/graphql.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ const {
2828
HackerpackInput,
2929
TravelGrantConfig,
3030
TravelGrantConfigInput,
31+
// MongoFile,
32+
// MongoFileInput,
3133
RegistrationSection,
3234
RegistrationSectionInput,
3335
EventTag,
@@ -297,6 +299,9 @@ const EventInput = new GraphQLInputObjectType({
297299
meetingRooms: {
298300
type: GraphQLList(MeetingRoomInput),
299301
},
302+
// map: {
303+
// type: MongoFileInput,
304+
// },
300305
/** DELETE LATER: test area */
301306
submissionFormQuestions: {
302307
type: GraphQLList(RegistrationSectionInput),
@@ -488,6 +493,9 @@ const EventType = new GraphQLObjectType({
488493
meetingRooms: {
489494
type: GraphQLList(MeetingRoom),
490495
},
496+
// map: {
497+
// type: MongoFile,
498+
// },
491499
// Implement userprofile in graphql
492500
// TODO: Figure this stuff out
493501
// winners: {

backend/modules/event/model.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const ChallengeSchema = require('@hackjunction/shared/schemas/Challenge')
1010
const HackerpackSchema = require('@hackjunction/shared/schemas/Hackerpack')
1111
const CloudinaryImageSchema = require('@hackjunction/shared/schemas/CloudinaryImage')
1212
const Certificate = require('@hackjunction/shared/schemas/Certificate')
13+
const mongoFile = require('@hackjunction/shared/schemas/MongoFile')
1314
const RegistrationSectionSchema = require('@hackjunction/shared/schemas/RegistrationSection')
1415
const TrackSchema = require('@hackjunction/shared/schemas/Track')
1516
const EventRecruitersSchema = require('@hackjunction/shared/schemas/Recruiter')
@@ -92,6 +93,7 @@ const EventSchema = new mongoose.Schema({
9293
coverImage: CloudinaryImageSchema.mongoose,
9394
logo: CloudinaryImageSchema.mongoose,
9495
certificate: Certificate.mongoose,
96+
//map: mongoFile.mongoose,
9597
/** Event configuration */
9698
eventType: {
9799
type: String,

backend/modules/team/controller.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,12 @@ controller.deleteTeam = (eventId, userId) => {
9797
})
9898
}
9999

100+
controller.deleteTeamByCode = (eventId, code) => {
101+
return controller.getTeamByCode(eventId, code).then(team => {
102+
return team.remove()
103+
})
104+
}
105+
100106
controller.editTeam = (eventId, userId, edits) => {
101107
return controller.getTeam(eventId, userId).then(team => {
102108
if (team.owner !== userId && !_.includes(team.members, userId)) {
@@ -217,10 +223,14 @@ controller.joinTeam = (eventId, userId, code) => {
217223
}
218224
//TODO: optimize this process, slow with over 200 teams
219225
controller.acceptCandidateToTeam = (eventId, userId, code, candidateId) => {
226+
220227
let teamToReturn
221228
return controller
222229
.getTeamByCode(eventId, code)
223230
.then(team => {
231+
if (team.members.length >= 4) {
232+
throw new ForbiddenError('Teams can have at most 5 members')
233+
}
224234
if (!_.includes([team.owner].concat(team.members), userId)) {
225235
throw new InsufficientPrivilegesError(
226236
'Only the team members can accept candidates',
@@ -580,4 +590,28 @@ controller.convertToFlatExportData = teamWithMeta => {
580590
}
581591
}
582592

593+
controller.organiserRemoveMemberFromTeam = (eventId, teamCode, userToRemove) => {
594+
console.log("removing ", eventId, teamCode, userToRemove)
595+
return controller.getTeamByCode(eventId, teamCode).then(team => {
596+
597+
if (team.members.length === 0 && team.owner === userToRemove) {
598+
console.log("deleting team")
599+
controller.deleteTeamByCode(eventId, teamCode)
600+
} else {
601+
if (team.owner === userToRemove) {
602+
console.log("new owner", team.members[0])
603+
team.owner = team.members[0]
604+
team.members = team.members.slice(1)
605+
} else {
606+
console.log("removing member")
607+
team.members = team.members.filter(member => member !== userToRemove)
608+
}
609+
console.log("deleted ", team.members)
610+
return team.save()
611+
}
612+
console.log("deleted team", team.members)
613+
return team.save()
614+
})
615+
}
616+
583617
module.exports = controller

backend/modules/team/routes.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,16 @@ const removeMember = asyncHandler(async (req, res) => {
7878
return res.status(200).json(team)
7979
})
8080

81+
const organiserRemoveMemberFromTeam = asyncHandler(async (req, res) => {
82+
const team = await TeamController.organiserRemoveMemberFromTeam(
83+
req.event._id,
84+
req.params.code,
85+
req.params.userId,
86+
)
87+
console.log("deleted: ", team)
88+
return res.status(200).json(team)
89+
})
90+
8191
const getTeam = asyncHandler(async (req, res) => {
8292
let team = await TeamController.getTeam(
8393
req.event._id.toString(),
@@ -196,6 +206,15 @@ router
196206
exportTeams,
197207
)
198208

209+
router
210+
.route('/organiser/:slug/:code/members/:userId')
211+
.delete(
212+
hasToken,
213+
hasPermission(Auth.Permissions.MANAGE_EVENT),
214+
isEventOrganiser,
215+
organiserRemoveMemberFromTeam,
216+
)
217+
199218
/** Participant-facing routes */
200219
router
201220
.route('/:slug')
291 KB
Loading
1.22 MB
Loading
1.17 MB
Loading
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
import { IconButton, Typography } from '@material-ui/core'
2+
3+
import Button from 'components/generic/Button'
4+
import React, { useMemo, useState, useEffect, useCallback } from 'react'
5+
import { useDispatch, useSelector } from 'react-redux'
6+
import { useRouteMatch, useLocation } from 'react-router'
7+
8+
import TeamHeader from '../TeamHeader'
9+
import TeamDescription from '../TeamDescription'
10+
import TeamRoles from '../TeamRoles'
11+
import TeamMembers from '../TeamMembers'
12+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
13+
import junctionStyle from 'utils/styles'
14+
import { popupCenter } from 'utils/misc'
15+
import { Email } from '@material-ui/icons'
16+
import { objToArr } from 'utils/dataModifiers'
17+
import PageWrapper from 'components/layouts/PageWrapper'
18+
import { gradientRandomizer } from 'utils/stylingHelpers'
19+
import TeamsService from 'services/teams'
20+
import * as SnackbarActions from 'redux/snackbar/actions'
21+
import * as OrganiserActions from 'redux/organiser/actions'
22+
import * as DashboardActions from 'redux/dashboard/actions'
23+
24+
25+
26+
27+
// TODO add socialLinks component from Damilare (@mrprotocoll)
28+
29+
export default ({
30+
enableActions = true,
31+
teamData = {},
32+
onRoleClick = () => { },
33+
onCancel = () => { },
34+
slug = '',
35+
}) => {
36+
const dispatch = useDispatch()
37+
const [loading, setLoading] = useState(false)
38+
const [teamMemberToRemove, setTeamMemberToRemove] = useState('')
39+
const match = useRouteMatch()
40+
console.log("match", slug)
41+
42+
const [teamMembersArr, setTeamMembersArr] = useState([...objToArr(teamData.meta)])
43+
const membersCount = teamData.members.length
44+
45+
const styling = {
46+
borderStyle: 'tw-border tw-border-solid tw-border-gray-300 tw-p-4',
47+
imageSize: 'tw-w-16 tw-h-16',
48+
alignment: 'tw-items-center',
49+
userProfile: {},
50+
}
51+
52+
53+
const onClickRemove = (userId) => {
54+
console.log("delete", slug, teamData.code, userId)
55+
}
56+
57+
const onClickDelete = () => {
58+
console.log("delete")
59+
}
60+
61+
62+
const handleRemove = useEffect(() => {
63+
console.log("delete", slug, teamData?.code, teamMemberToRemove)
64+
setLoading(true)
65+
dispatch(DashboardActions.organiserRemoveMemberFromTeam(slug, teamData.code, teamMemberToRemove))
66+
.then((team) => {
67+
console.log("removed succesfully.", team)
68+
dispatch(OrganiserActions.updateTeamsForEvent(slug))
69+
})
70+
.then(() => {
71+
console.log("teams updated", teamMembersArr.filter(t => t.profile.userId !== teamMemberToRemove))
72+
setTeamMembersArr(teamMembersArr.filter(t => t.profile.userId !== teamMemberToRemove))
73+
dispatch(SnackbarActions.success('removed ' + teamMemberToRemove + ' from team ' + teamData?.code))
74+
})
75+
.catch(() => {
76+
dispatch(
77+
SnackbarActions.error(
78+
'Something went wrong... please try again.',
79+
),
80+
)
81+
})
82+
.finally(() => {
83+
setTeamMemberToRemove('')
84+
setLoading(false)
85+
})
86+
}, [teamMemberToRemove, slug, teamData?.code, dispatch])
87+
88+
89+
90+
const classes = junctionStyle()
91+
return (
92+
<PageWrapper loading={loading}>
93+
<div className="tw-flex tw-flex-col tw-gap-12">
94+
<div className="tw tw-bg-gradient-to-r tw-from-teal-400 tw-to-blue-500 tw-w-full tw-h-16 tw-rounded-lg"></div>
95+
<div className="tw-flex tw-flex-col tw-gap-8">
96+
<TeamHeader
97+
teamName={teamData.name}
98+
teamChallenge={teamData.challenge}
99+
teamCode={enableActions ? teamData.code : null}
100+
/>
101+
<TeamDescription
102+
teamSubtitle={teamData?.subtitle}
103+
teamDescription={teamData?.description}
104+
teamIdea={teamData?.ideaTitle}
105+
teamIdeaDescription={teamData?.ideaDescription}
106+
/>
107+
</div>
108+
<TeamRoles
109+
maxRoles={10}
110+
profileView
111+
teamRoles={teamData.teamRoles}
112+
onRoleClick={onRoleClick}
113+
/>
114+
{teamMembersArr?.map(userProfile => {
115+
console.log("userProfile.profile?.avatar", userProfile.profile?.avatar)
116+
return (
117+
<div
118+
className={`tw-flex tw-justify-between tw-rounded-lg ${styling.borderStyle} ${styling.alignment}`}
119+
>
120+
<div className="tw-flex tw-gap-4 tw-items-end">
121+
<div
122+
className={`tw-bg-gradient-to-r ${gradientRandomizer()} tw-rounded-full ${styling.imageSize
123+
} tw-bg-cover`}
124+
style={{ backgroundImage: `url(${userProfile.profile?.avatar})` }}
125+
></div>
126+
<div className="tw-flex tw-flex-col tw-items-start tw-gap-1">
127+
<Typography
128+
className="tw-tracking-tight tw-font-medium"
129+
variant="h5"
130+
component="h5"
131+
>
132+
{userProfile.profile.firstName}{' '}
133+
{userProfile.profile.lastName}
134+
</Typography>
135+
136+
137+
</div>
138+
139+
</div>
140+
{membersCount > 0 ? (
141+
<Button
142+
onClick={() => setTeamMemberToRemove(userProfile.profile.userId)}
143+
color="error"
144+
variant="contained"
145+
>
146+
remove from team
147+
</Button>
148+
) : (
149+
<Button
150+
onClick={() => setTeamMemberToRemove(userProfile.profile.userId)}
151+
color="error"
152+
variant="contained"
153+
>
154+
Delete the team
155+
</Button>
156+
)}
157+
</div>
158+
)
159+
}
160+
)}
161+
<div className="tw-flex tw-content-center tw-justify-start">
162+
{teamData?.discord && (
163+
<FontAwesomeIcon
164+
icon={['fab', 'discord']}
165+
onClick={() =>
166+
popupCenter({
167+
url: teamData.discord,
168+
title: 'Discord',
169+
})
170+
}
171+
className={classes.socialIcon}
172+
size="2x"
173+
/>
174+
)}
175+
{teamData?.telegram && (
176+
<FontAwesomeIcon
177+
icon={['fab', 'telegram']}
178+
onClick={() =>
179+
popupCenter({
180+
url: teamData.telegram,
181+
title: 'Telegram',
182+
})
183+
}
184+
className={classes.socialIcon}
185+
size="2x"
186+
/>
187+
)}
188+
{teamData?.email && (
189+
<IconButton
190+
color="primary"
191+
aria-label="Email"
192+
className="tw-p-0"
193+
onClick={() =>
194+
popupCenter({
195+
url: `mailto:${teamData.email}`,
196+
title: 'email',
197+
})
198+
}
199+
>
200+
<Email className={classes.socialIcon} />
201+
</IconButton>
202+
)}
203+
</div>
204+
{/* TODO add socialLinks component from Damilare (@mrprotocoll) */}
205+
<div className='tw-p-4'>
206+
<Button
207+
onClick={onCancel}
208+
color="primary"
209+
variant="contained"
210+
>
211+
cancel
212+
</Button>
213+
</div>
214+
</div>
215+
</PageWrapper>
216+
)
217+
}
218+
//TODO fix issue that doesn't let team owners leave their own team

frontend/src/components/Team/TeamProfile/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ export default ({
1818
enableActions = true,
1919
teamData = {},
2020
onClickLeave,
21-
onClickDelete = () => {},
21+
onClickDelete = () => { },
2222
onClickEdit,
23-
onRoleClick = () => {},
23+
onRoleClick = () => { },
2424
loading = false,
2525
}) => {
2626
const teamMembersArr = [...objToArr(teamData.meta)]

frontend/src/components/generic/_Table/Table.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ const _Table = ({
319319
_Table.defaultProps = {
320320
data: [],
321321
columns: [],
322-
onRowClick: () => {},
322+
onRowClick: () => { },
323323
bulkActions: [],
324324
enableExport: true,
325325
enablePagination: true,

0 commit comments

Comments
 (0)