Skip to content

Commit

Permalink
feat(app): Proposal filtering (#7)
Browse files Browse the repository at this point in the history
Co-authored-by: Roland Schlaefli <[email protected]>
  • Loading branch information
mxmlnwbr and rschlaefli authored Nov 9, 2023
1 parent 6992510 commit 9f61c15
Show file tree
Hide file tree
Showing 8 changed files with 290 additions and 159 deletions.
7 changes: 4 additions & 3 deletions src/components/ProposalStatusForm.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
import { useSessionStorage } from '@uidotdev/usehooks'
import { Tabs, UserNotification } from '@uzh-bf/design-system'
import type { Session } from 'next-auth'
import { useSession } from 'next-auth/react'
import { ProposalDetails } from 'src/types/app'
import AcceptProposalForm from './AcceptProposalForm' // Import AcceptProposalForm and other form components
import DeclineProposalForm from './DeclineProposalForm'
import RejectProposalForm from './RejectProposalForm'
import TentativeAcceptProposalForm from './TentativeAcceptProposalForm'
interface ProposalStatusFormProps {
proposalDetails: ProposalDetails
session: Session | null
}

export default function ProposalStatusForm({
proposalDetails,
session,
}: ProposalStatusFormProps) {
const { data: session } = useSession()

const [providedFeedback, setProvidedFeedback] = useSessionStorage<
null | string
>(proposalDetails.id, null)

if (
(proposalDetails?.typeKey === 'STUDENT' &&
proposalDetails?.statusKey === 'MATCHED_TENTATIVE') ||
Expand Down
64 changes: 54 additions & 10 deletions src/components/StudentProposals.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,72 @@
import { H2, H3 } from '@uzh-bf/design-system'
import { RefObject } from 'react'
import { H2, H3, Select } from '@uzh-bf/design-system'
import * as R from 'ramda'
import { RefObject, useMemo } from 'react'
import { ProposalDetails, ProposalStatusFilter } from 'src/types/app'
import ProposalCard from './ProposalCard'

interface StudentProposalsProps {
data: any
groupedStudentProposals: any
data: ProposalDetails[]
selectedProposal: string | null
setSelectedProposal: (proposalId: string | null) => void
setDisplayMode: (displayMode: string) => void
buttonRef: RefObject<HTMLButtonElement>
filters: {
status: ProposalStatusFilter
}
setFilters: (filters: { status: ProposalStatusFilter }) => void
}

export default function StudentProposals({
data,
groupedStudentProposals,
selectedProposal,
setSelectedProposal,
setDisplayMode,
buttonRef,
filters,
setFilters,
}: StudentProposalsProps) {
const groupedStudentProposals = useMemo(() => {
if (!data) return {}

return R.groupBy<ProposalDetails>(
(p) => p.topicArea.name,
R.sortBy(
R.prop('title'),
data.filter(
(proposal: ProposalDetails) => proposal.typeKey === 'STUDENT'
)
)
)
}, [data])

return (
<div>
<H2>Student Proposals</H2>
<div className="flex items-center justify-between">
<H2>Student Proposals</H2>
<Select
value={filters.status}
items={[
{
value: ProposalStatusFilter.OPEN_PROPOSALS,
label: 'Open Proposals',
},
{
value: ProposalStatusFilter.MY_PROPOSALS,
label: 'My Proposals',
},
{
value: ProposalStatusFilter.REJECTED_AND_DECLINED_PROPOSALS,
label: 'Rejected / Declined Proposals',
},
{
value: ProposalStatusFilter.ALL_PROPOSALS,
label: 'All Proposals',
},
]}
onChange={(newStatus: string) => {
setFilters({ status: newStatus as ProposalStatusFilter })
setSelectedProposal(null)
}}
/>
</div>
<div className="text-base">
{data?.filter((proposal: any) => proposal.typeKey === 'STUDENT')
.length === 0 && <div>No student proposals available...</div>}
Expand All @@ -46,8 +91,7 @@ export default function StudentProposals({
proposal={proposal}
isActive={selectedProposal === proposal.id}
onClick={() => {
setSelectedProposal(proposal.id),
setDisplayMode('details')
setSelectedProposal(proposal.id)
buttonRef?.current?.scrollIntoView({
behavior: 'smooth',
})
Expand Down
7 changes: 3 additions & 4 deletions src/components/SupervisorProposals.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { H2 } from '@uzh-bf/design-system'
import { RefObject } from 'react'
import { ProposalDetails } from 'src/types/app'
import ProposalCard from './ProposalCard'

interface SupervisorProposalsProps {
isSupervisor: boolean
data: any
data: ProposalDetails[]
selectedProposal: string | null
setSelectedProposal: (proposalId: string | null) => void
setDisplayMode: (displayMode: string) => void
buttonRef: RefObject<HTMLButtonElement>
}

Expand All @@ -16,7 +16,6 @@ export default function SupervisorProposals({
data,
selectedProposal,
setSelectedProposal,
setDisplayMode,
buttonRef,
}: SupervisorProposalsProps) {
return (
Expand All @@ -35,7 +34,7 @@ export default function SupervisorProposals({
proposal={proposal}
isActive={selectedProposal === proposal.id}
onClick={() => {
setSelectedProposal(proposal.id), setDisplayMode('details')
setSelectedProposal(proposal.id)
buttonRef.current?.scrollIntoView({
behavior: 'smooth',
})
Expand Down
File renamed without changes.
18 changes: 18 additions & 0 deletions src/lib/hooks/useUserRole.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useSession } from 'next-auth/react'
import { UserRole } from '../constants'

function useUserRole() {
const { data: session } = useSession()

const isAdmin = session?.user?.role === UserRole.ADMIN
const isSupervisor = session?.user?.role === UserRole.SUPERVISOR
const isStudent = !isAdmin && !isSupervisor

return {
isAdmin,
isSupervisor,
isStudent,
}
}

export default useUserRole
83 changes: 36 additions & 47 deletions src/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,70 +1,59 @@
import { IconDefinition, faFilePdf } from '@fortawesome/free-regular-svg-icons'
import { useSession } from 'next-auth/react'
import { useRouter } from 'next/router'
import * as R from 'ramda'
import { useMemo, useRef, useState } from 'react'
import ProposalApplication from 'src/components/ProposalApplication'
import ProposalFeedback from 'src/components/ProposalFeedback'
import ProposalMeta from 'src/components/ProposalMeta'
import ProposalStatusForm from 'src/components/ProposalStatusForm'
import StudentProposals from 'src/components/StudentProposals'
import SupervisorProposals from 'src/components/SupervisorProposals'
import { UserRole } from 'src/lib/constants'
import useUserRole from 'src/lib/hooks/useUserRole'
import { trpc } from 'src/lib/trpc'
import { ProposalDetails } from 'src/types/app'
import { ProposalStatusFilter } from 'src/types/app'

const FileTypeIconMap: Record<string, IconDefinition> = {
'application/pdf': faFilePdf,
}
export default function Index() {
const router = useRouter()
const buttonRef = useRef<null | HTMLDivElement>(null)

const { data: session } = useSession()
const [filters, setFilters] = useState<{
status: ProposalStatusFilter
}>({
status: ProposalStatusFilter.OPEN_PROPOSALS,
})

const { isAdmin, isStudent, isSupervisor } = useUserRole()

const { data, isLoading, isError, isFetching } = trpc.proposals.useQuery()
const { data, isLoading, isError, isFetching } = trpc.proposals.useQuery({
filters,
})

const [displayMode, setDisplayMode] = useState('')
const [selectedProposal, setSelectedProposal] = useState<string | null>(
(router?.query?.proposalId as string) ?? null
)

const groupedStudentProposals = useMemo(() => {
if (!data) return []
return R.groupBy<ProposalDetails>(
(p) => p.topicArea.name,
R.sortBy(
R.prop('title'),
data.filter((proposal) => proposal.typeKey === 'STUDENT')
)
)
}, [data])

const proposalDetails = useMemo(() => {
if (!selectedProposal) return setSelectedProposal(data?.[0]?.id as string)
if (!selectedProposal) {
setSelectedProposal(data?.[0]?.id as string)
return
}

return data?.find((p) => p.id === selectedProposal)
}, [data, selectedProposal, displayMode])
}, [data, selectedProposal])

if (isLoading) {
return <div className="p-2">Loading 🔄🚀</div>
}

const isAdmin = session?.user?.role === UserRole.ADMIN
const isSupervisor = session?.user?.role === UserRole.SUPERVISOR
const isStudent = !isAdmin && !isSupervisor

return (
<div className="grid grid-cols-1 gap-2 m-4 md:grid-cols-2 flex-1">
<div className="grid flex-1 grid-cols-1 gap-2 m-4 md:grid-cols-2">
<div className="flex-initial pb-4 space-y-4 md:flex-1">
{isSupervisor && (
<StudentProposals
data={data}
groupedStudentProposals={groupedStudentProposals}
selectedProposal={selectedProposal}
setSelectedProposal={setSelectedProposal}
setDisplayMode={setDisplayMode}
buttonRef={buttonRef}
filters={filters}
setFilters={setFilters}
/>
)}

Expand All @@ -73,28 +62,28 @@ export default function Index() {
data={data}
selectedProposal={selectedProposal}
setSelectedProposal={setSelectedProposal}
setDisplayMode={setDisplayMode}
buttonRef={buttonRef}
/>
</div>

<div className="mb-4 border shadow" ref={buttonRef}>
{!selectedProposal && <div className="p-4">No proposal selected</div>}
<ProposalMeta proposalDetails={proposalDetails} />
<ProposalApplication
proposalDetails={proposalDetails}
isStudent={isStudent}
isSupervisor={isSupervisor}
/>
<ProposalFeedback
proposalDetails={proposalDetails}
isSupervisor={isSupervisor}
isAdmin={isAdmin}
/>
<ProposalStatusForm
proposalDetails={proposalDetails}
session={session}
/>
{proposalDetails && (
<>
<ProposalMeta proposalDetails={proposalDetails} />
<ProposalApplication
proposalDetails={proposalDetails}
isStudent={isStudent}
isSupervisor={isSupervisor}
/>
<ProposalFeedback
proposalDetails={proposalDetails}
isSupervisor={isSupervisor}
isAdmin={isAdmin}
/>
<ProposalStatusForm proposalDetails={proposalDetails} />
</>
)}
</div>
</div>
)
Expand Down
Loading

0 comments on commit 9f61c15

Please sign in to comment.