Skip to content

Commit

Permalink
Merge pull request #196 from ls1intum/70-implement-submit-application…
Browse files Browse the repository at this point in the history
…-page

Implement new Thesis Application Flow
  • Loading branch information
fabian-emilius authored Aug 26, 2024
2 parents d6c118e + 1c44919 commit 3dd9ba3
Show file tree
Hide file tree
Showing 52 changed files with 1,736 additions and 294 deletions.
1 change: 0 additions & 1 deletion .github/workflows/dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ name: Build and Deploy to Dev

on:
pull_request:
branches: [main]

jobs:
build-dev-container:
Expand Down
1 change: 1 addition & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"globals": "15.9.0",
"html-webpack-plugin": "5.6.0",
"mini-css-extract-plugin": "2.9.0",
"postcss": "8.4.41",
"postcss-loader": "8.1.1",
"postcss-preset-mantine": "1.17.0",
"prettier": "3.3.3",
Expand Down
23 changes: 6 additions & 17 deletions client/src/app/Routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,10 @@ const ThesisOverviewPage = lazy(() => import('../pages/ThesisOverviewPage/Thesis
const DashboardPage = lazy(() => import('../pages/DashboardPage/DashboardPage'))
const LogoutPage = lazy(() => import('../pages/LogoutPage/LogoutPage'))
const MyInformationPage = lazy(() => import('../pages/MyInformationPage/MyInformationPage'))
const SubmitApplicationStepOnePage = lazy(
() => import('../pages/SubmitApplicationPage/SubmitApplicationStepOnePage'),
const SubmitApplicationPage = lazy(
() => import('../pages/SubmitApplicationPage/SubmitApplicationPage'),
)
const SubmitApplicationStepTwoPage = lazy(
() => import('../pages/SubmitApplicationPage/SubmitApplicationStepTwoPage'),
)
const ManageTopicPage = lazy(() => import('../pages/ManageTopicsPage/ManageTopicsPage'))
const ManageTopicsPage = lazy(() => import('../pages/ManageTopicsPage/ManageTopicsPage'))
const TopicPage = lazy(() => import('../pages/TopicPage/TopicPage'))
const ReviewApplicationPage = lazy(
() => import('../pages/ReviewApplicationPage/ReviewApplicationPage'),
Expand Down Expand Up @@ -50,26 +47,18 @@ const AppRoutes = () => {
}
/>
<Route
path='/submit-application/pick-topic'
element={
<AuthenticatedArea>
<SubmitApplicationStepOnePage />
</AuthenticatedArea>
}
/>
<Route
path='/submit-application/apply/:topic_id?'
path='/submit-application/:topicId?'
element={
<AuthenticatedArea>
<SubmitApplicationStepTwoPage />
<SubmitApplicationPage />
</AuthenticatedArea>
}
/>
<Route
path='/topics'
element={
<AuthenticatedArea requiredGroups={['admin', 'advisor', 'supervisor']}>
<ManageTopicPage />
<ManageTopicsPage />
</AuthenticatedArea>
}
/>
Expand Down
12 changes: 10 additions & 2 deletions client/src/app/layout/AuthenticatedArea/AuthenticatedArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import {
SignOut,
Sun,
FolderSimplePlus,
User,
PaperPlaneTilt,
} from 'phosphor-react'
import { useIsSmallerBreakpoint } from '../../../hooks/theme'
import { useAuthenticationContext } from '../../../hooks/authentication'
Expand All @@ -42,6 +44,12 @@ const links: Array<{
groups: string[] | undefined
}> = [
{ link: '/dashboard', label: 'Dashboard', icon: NewspaperClipping, groups: undefined },
{
link: '/submit-application',
label: 'Submit Application',
icon: PaperPlaneTilt,
groups: undefined,
},
{
link: '/applications',
label: 'Review Applications',
Expand Down Expand Up @@ -159,14 +167,14 @@ const AuthenticatedArea = (props: PropsWithChildren<IAuthenticatedAreaProps>) =>
))}
</AppShell.Section>
<AppShell.Section>
{/*<Link
<Link
to='/settings/my-information'
className={classes.link}
data-active={location.pathname.startsWith('/settings/my-information') || undefined}
>
<User className={classes.linkIcon} size={32} />
<span>My Information</span>
</Link>*/}
</Link>
<Link to='/logout' className={classes.link}>
<SignOut className={classes.linkIcon} size={32} />
<span>Logout</span>
Expand Down
14 changes: 9 additions & 5 deletions client/src/components/ApplicationData/ApplicationData.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IApplication } from '../../requests/responses/application'
import { Stack, Group, Divider, Grid, Title, Badge } from '@mantine/core'
import { Stack, Group, Divider, Grid, Title, Badge, Accordion } from '@mantine/core'
import AuthenticatedFilePreview from '../AuthenticatedFilePreview/AuthenticatedFilePreview'
import React, { ReactNode } from 'react'
import { GLOBAL_CONFIG } from '../../config/global'
Expand All @@ -8,6 +8,7 @@ import { formatApplicationState, formatDate, formatUser } from '../../utils/form
import LabeledItem from '../LabeledItem/LabeledItem'
import DocumentEditor from '../DocumentEditor/DocumentEditor'
import { ApplicationStateColor } from '../../config/colors'
import TopicAccordionItem from '../TopicAccordionItem/TopicAccordionItem'

interface IApplicationDataProps {
application: IApplication
Expand All @@ -28,10 +29,13 @@ const ApplicationData = (props: IApplicationDataProps) => {
</Title>
{rightTitleSection}
</Group>
<LabeledItem
label='Thesis Title'
value={application.topic?.title || application.thesisTitle}
/>
{application.topic ? (
<Accordion variant='separated'>
<TopicAccordionItem topic={application.topic} />
</Accordion>
) : (
<LabeledItem label='Thesis Title' value={application.thesisTitle} />
)}
<LabeledItem
label='Motivation'
value={<DocumentEditor value={application.motivation} />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const ApplicationsTable = (props: IApplicationsTableProps) => {
title: 'Suggested Title',
ellipsis: true,
width: 300,
render: (application) => application.thesisTitle || application.topic?.title,
},
reviewed_at: {
accessor: 'reviewedAt',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { useEffect, useMemo, useState } from 'react'
import { doRequest } from '../../requests/request'
import { useMemo, useState } from 'react'
import { Button, Space, Stack, Text } from '@mantine/core'
import { downloadPdf } from '../../utils/blob'
import { showSimpleError } from '../../utils/notification'
import { getApiResponseErrorMessage } from '../../requests/handler'
import { downloadFile } from '../../utils/blob'
import { useApiFile } from '../../hooks/fetcher'

interface IAuthenticatedIframeProps {
url: string
Expand All @@ -16,27 +14,9 @@ interface IAuthenticatedIframeProps {
const AuthenticatedFilePreview = (props: IAuthenticatedIframeProps) => {
const { url, filename, allowDownload = true, title, height } = props

const [file, setFile] = useState<Blob>()
const [file, setFile] = useState<File>()

useEffect(() => {
setFile(undefined)

return doRequest<Blob>(
url,
{
method: 'GET',
requiresAuth: true,
responseType: 'blob',
},
(res) => {
if (res.ok) {
setFile(res.data)
} else {
showSimpleError(getApiResponseErrorMessage(res))
}
},
)
}, [url])
useApiFile(url, filename, setFile)

const iframeUrl = useMemo(() => {
return file ? `${URL.createObjectURL(file)}#toolbar=0&navpanes=0` : undefined
Expand All @@ -49,7 +29,7 @@ const AuthenticatedFilePreview = (props: IAuthenticatedIframeProps) => {
{allowDownload && (
<>
<Space mb='md' />
<Button variant='outline' mx='auto' onClick={() => file && downloadPdf(file, filename)}>
<Button variant='outline' mx='auto' onClick={() => file && downloadFile(file)}>
Download
</Button>
</>
Expand Down
24 changes: 24 additions & 0 deletions client/src/components/TopicAccordionItem/TopicAccordionItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Accordion } from '@mantine/core'
import TopicData from '../TopicData/TopicData'
import React, { PropsWithChildren } from 'react'
import { ITopic } from '../../requests/responses/topic'

interface ITopicAccordionItemProps {
topic: ITopic
}

const TopicAccordionItem = (props: PropsWithChildren<ITopicAccordionItemProps>) => {
const { topic, children } = props

return (
<Accordion.Item key={topic.topicId} value={topic.topicId}>
<Accordion.Control>{topic.title}</Accordion.Control>
<Accordion.Panel>
<TopicData topic={topic} />
{children}
</Accordion.Panel>
</Accordion.Item>
)
}

export default TopicAccordionItem
55 changes: 55 additions & 0 deletions client/src/components/TopicData/TopicData.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { ITopic } from '../../requests/responses/topic'
import { Grid, Stack } from '@mantine/core'
import LabeledItem from '../LabeledItem/LabeledItem'
import { formatDate, formatUser } from '../../utils/format'
import { GLOBAL_CONFIG } from '../../config/global'
import DocumentEditor from '../DocumentEditor/DocumentEditor'
import React from 'react'

interface ITopicDataProps {
topic: ITopic
}

const TopicData = (props: ITopicDataProps) => {
const { topic } = props

return (
<Stack gap='md'>
<Grid>
<Grid.Col span={{ md: 3 }}>
<LabeledItem
label='Supervisor'
value={topic.supervisors
.map((user) => formatUser(user, { withUniversityId: false }))
.join(', ')}
/>
</Grid.Col>
<Grid.Col span={{ md: 3 }}>
<LabeledItem
label='Advisor'
value={topic.advisors
.map((user) => formatUser(user, { withUniversityId: false }))
.join(', ')}
/>
</Grid.Col>
<Grid.Col span={{ md: 3 }}>
<LabeledItem
label='Type'
value={topic.type ? (GLOBAL_CONFIG.thesis_types[topic.type] ?? topic.type) : 'Any'}
/>
</Grid.Col>
<Grid.Col span={{ md: 3 }}>
<LabeledItem
label='Published At'
value={formatDate(topic.createdAt, { withTime: false })}
/>
</Grid.Col>
</Grid>
<DocumentEditor label='Problem Statement' value={topic.problemStatement} />
{topic.goals && <DocumentEditor label='Goals' value={topic.goals} />}
{topic.references && <DocumentEditor label='References' value={topic.references} />}
</Stack>
)
}

export default TopicData
25 changes: 9 additions & 16 deletions client/src/components/TopicsTable/TopicsTable.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React, { ReactNode } from 'react'
import { useAutoAnimate } from '@formkit/auto-animate/react'
import { DataTable, DataTableColumn } from 'mantine-datatable'
import { formatDate, formatUser } from '../../utils/format'
Expand All @@ -7,15 +6,15 @@ import { ITopic } from '../../requests/responses/topic'
import { useNavigate } from 'react-router-dom'
import { Badge } from '@mantine/core'

type TopicColumn = 'title' | 'advisor' | 'supervisor' | 'actions' | 'state' | 'createdAt'
type TopicColumn = 'title' | 'advisor' | 'supervisor' | 'state' | 'createdAt' | string

interface ITopicsTableProps {
columns?: TopicColumn[]
actions?: (topic: ITopic) => ReactNode
extraColumns?: Record<string, DataTableColumn<ITopic>>
}

const TopicsTable = (props: ITopicsTableProps) => {
const { actions, columns = ['title', 'supervisor', 'advisor'] } = props
const { extraColumns, columns = ['title', 'supervisor', 'advisor'] } = props

const navigate = useNavigate()
const [bodyRef] = useAutoAnimate<HTMLTableSectionElement>()
Expand All @@ -35,31 +34,25 @@ const TopicsTable = (props: ITopicsTableProps) => {
accessor: 'title',
title: 'Title',
ellipsis: true,
width: 300,
width: 350,
},
supervisor: {
accessor: 'supervisor',
title: 'Supervisor',
render: (topic) => topic.supervisors.map((user) => formatUser(user)).join(', '),
render: (topic) =>
topic.supervisors.map((user) => formatUser(user, { withUniversityId: false })).join(', '),
},
advisor: {
accessor: 'advisor',
title: 'Advisor',
render: (topic) => topic.advisors.map((user) => formatUser(user)).join(', '),
render: (topic) =>
topic.advisors.map((user) => formatUser(user, { withUniversityId: false })).join(', '),
},
createdAt: {
accessor: 'createdAt',
title: 'Created At',
render: (record) => formatDate(record.createdAt),
},
actions: {
accessor: 'actions',
title: 'Actions',
textAlign: 'center',
noWrap: true,
width: 120,
render: (topic) => actions?.(topic),
},
}

return (
Expand All @@ -79,7 +72,7 @@ const TopicsTable = (props: ITopicsTableProps) => {
bodyRef={bodyRef}
records={topics?.content}
idAccessor='topicId'
columns={columns.map((column) => columnConfig[column])}
columns={columns.map((column) => columnConfig[column] || extraColumns?.[column])}
onRowClick={({ record }) => navigate(`/topics/${record.topicId}`)}
/>
)
Expand Down
Loading

0 comments on commit 3dd9ba3

Please sign in to comment.