Skip to content

Commit

Permalink
Feature: Wharton application cycle member editing
Browse files Browse the repository at this point in the history
  • Loading branch information
julianweng committed Nov 5, 2023
1 parent 37a0eef commit 0e9bc87
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 8 deletions.
8 changes: 3 additions & 5 deletions backend/clubs/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@
router.register(
r"cycles", WhartonCyclesView, basename="wharton-applications-create",
)
router.register(
r"whartonapplications", WhartonApplicationAPIView, basename="wharton",
)
router.register(r"submissions", ApplicationSubmissionUserViewSet, basename="submission")

clubs_router = routers.NestedSimpleRouter(router, r"clubs", lookup="club")
Expand Down Expand Up @@ -155,11 +158,6 @@
MeetingZoomWebhookAPIView.as_view(),
name="webhooks-meeting",
),
path(
r"whartonapplications/",
WhartonApplicationAPIView.as_view(),
name="wharton-applications",
),
path(
r"whartonapplications/status/",
WhartonApplicationStatusAPIView.as_view(),
Expand Down
45 changes: 43 additions & 2 deletions backend/clubs/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4853,7 +4853,6 @@ def get_serializer_class(self):
return ClubApplicationSerializer

def get_queryset(self):

return (
ClubApplication.objects.filter(club__code=self.kwargs["club_code"],)
.select_related("application_cycle", "club")
Expand All @@ -4866,6 +4865,7 @@ def get_queryset(self):
class WhartonCyclesView(viewsets.ModelViewSet):
"""
patch: Update application cycle and WC applications with cycle
clubs: list clubs with cycle
"""

permission_classes = [WhartonApplicationPermission | IsSuperuser]
Expand Down Expand Up @@ -4906,11 +4906,52 @@ def update(self, *args, **kwargs):
ClubApplication.objects.bulk_update(applications, f)
return super().update(*args, **kwargs)

@action(detail=True, methods=["get"])
def clubs(self, *args, **kwargs):
cycle = self.get_object()
data = ClubApplication.objects.filter(
is_wharton_council=True, application_cycle=cycle,
)
return Response(ClubApplicationSerializer(data, many=True).data)

class WhartonApplicationAPIView(generics.ListAPIView):
@action(detail=True, methods=["post"])
def add_clubs(self, *args, **kwargs):
cycle = self.get_object()
print(self.request.data)
club_ids = self.request.data.get("clubs")
start = cycle.start_date
end = cycle.end_date
apps = ClubApplication.objects.filter(pk__in=club_ids)
for app in apps:
app.application_cycle = cycle
app.application_start_time = start
app.application_end_time = end
ClubApplication.objects.bulk_update(
apps,
["application_cycle", "application_start_time", "application_end_time"],
)
return Response([])

@action(detail=False, methods=["post"])
def remove_clubs_from_all(self, *args, **kwargs):
club_ids = self.request.data.get("clubs", [])
print(self.request.data)
apps = ClubApplication.objects.filter(pk__in=club_ids)
for app in apps:
app.application_cycle = None
ClubApplication.objects.bulk_update(
apps,
["application_cycle", "application_start_time", "application_end_time"],
)
return Response([])


class WhartonApplicationAPIView(viewsets.ModelViewSet):
"""
get: Return information about all Wharton Council club applications which are
currently on going
cycle: Add application to given cycle
"""

permission_classes = [IsAuthenticated]
Expand Down
120 changes: 119 additions & 1 deletion frontend/components/Settings/WhartonApplicationCycles.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { Field } from 'formik'
import React, { ReactElement } from 'react'
import React, { ReactElement, useEffect } from 'react'
import Select from 'react-select'

import { ClubApplication } from '~/types'
import { doApiRequest } from '~/utils'

import { Icon, Loading, Modal } from '../common'
import { DateTimeField, TextField } from '../FormComponents'
import ModelForm from '../ModelForm'

Expand All @@ -12,7 +17,86 @@ const fields = (
</>
)

type ClubOption = {
label: string
value: number
}

const WhartonApplicationCycles = (): ReactElement => {
const [editMembership, setEditMembership] = React.useState(false)
const [membershipCycle, setMembershipCycle] = React.useState({
name: '',
id: null,
})

// use { label: string; value: number; }[]
const [clubsSelected, setClubsSelected] = React.useState<ClubOption[]>([])

const [clubsInitial, setClubsInitial] = React.useState(null)
const [clubsInitialOptions, setClubsInitialOptions] = React.useState<
ClubOption[]
>([])
const [possibleClubs, setPossibleClubs] = React.useState(null)
const [clubOptions, setClubOptions] = React.useState(null)

const closeModal = (): void => {
setEditMembership(false)
// calculate difference between initial and selected
const clubsToRemove = clubsInitialOptions.filter(
(x) => !clubsSelected.includes(x),
)
const clubsToAdd = clubsSelected.filter(
(x) => !clubsInitialOptions.includes(x),
)

// call /cycles/:id/add_clubs and /cycles/remove_clubs_from_all with data.clubs as list of ids
if (clubsToRemove.length > 0) {
doApiRequest(`/cycles/remove_clubs_from_all/`, {
method: 'POST',
body: { clubs: clubsToRemove.map((x) => x.value) },
})
}
if (clubsToAdd.length > 0) {
doApiRequest(`/cycles/${membershipCycle.id}/add_clubs/`, {
method: 'POST',
body: { clubs: clubsToAdd.map((x) => x.value) },
})
}
}
useEffect(() => {
if (possibleClubs == null) {
doApiRequest('/whartonapplications/?format=json')
.then((resp) => resp.json())
.then((data) => {
setPossibleClubs(data)
setClubOptions(
data.map((club: ClubApplication) => {
return { label: club.name, value: club.id }
}),
)
})
}
}, [possibleClubs])

useEffect(() => {
if (membershipCycle && membershipCycle.id != null) {
doApiRequest(`/cycles/${membershipCycle.id}/clubs?format=json`)
.then((resp) => resp.json())
.then((data) => {
setClubsInitial(data)
const initialOptions = data.map((club: ClubApplication) => {
return { label: club.name, value: club.id }
})
setClubsInitialOptions(initialOptions)
setClubsSelected(initialOptions)
})
}
}, [membershipCycle])

if (clubOptions == null) {
return <Loading />
}

return (
<>
<ModelForm
Expand All @@ -24,7 +108,41 @@ const WhartonApplicationCycles = (): ReactElement => {
{ name: 'start_date' },
{ name: 'end_date' },
]}
actions={(object) => (
<button
className="button is-info is-small"
onClick={() => {
setMembershipCycle(object)
setEditMembership(true)
}}
>
<Icon name="user" /> Membership
</button>
)}
/>
<Modal show={editMembership} closeModal={closeModal}>
{membershipCycle && membershipCycle.name && (
<>
<h1 style={{ paddingBottom: '20px' }}>
Club Membership for {membershipCycle.name}
</h1>
<div style={{ paddingLeft: 10, paddingRight: 10 }}>
<Select
onChange={(e) => setClubsSelected([...e])}
value={clubsSelected}
options={clubOptions}
isMulti
/>
</div>
<button
className="button is-primary"
style={{ position: 'absolute', bottom: 10, right: 10 }}
>
Submit
</button>
</>
)}
</Modal>
</>
)
}
Expand Down

0 comments on commit 0e9bc87

Please sign in to comment.