mirror of
https://github.com/UniversityOfHelsinkiCS/oodikone.git
synced 2026-10-12 19:03:39 +03:00
[Study programme] Rewrite progress section with MUI
This commit is contained in:
-62
@@ -1,62 +0,0 @@
|
||||
import accessibility from 'highcharts/modules/accessibility'
|
||||
import exportData from 'highcharts/modules/export-data'
|
||||
import exporting from 'highcharts/modules/exporting'
|
||||
import ReactHighcharts from 'react-highcharts'
|
||||
|
||||
import { generateGradientColors } from '@/util/color'
|
||||
|
||||
exporting(ReactHighcharts.Highcharts)
|
||||
exportData(ReactHighcharts.Highcharts)
|
||||
accessibility(ReactHighcharts.Highcharts)
|
||||
|
||||
interface BarChartProps {
|
||||
data: {
|
||||
creditGraphStats: Record<string, { name: string; data: number[] }[]>
|
||||
years: string[]
|
||||
}
|
||||
track: string
|
||||
}
|
||||
|
||||
export const BarChart = ({ data, track }: BarChartProps) => {
|
||||
if (!data?.creditGraphStats?.[track]) {
|
||||
return null
|
||||
}
|
||||
|
||||
const correctData = data.creditGraphStats[track]
|
||||
const colors = generateGradientColors(correctData.length)
|
||||
const dataWithColors = Object.values(correctData).map((series, index) => ({
|
||||
...series,
|
||||
color: colors[index],
|
||||
type: 'column' as const,
|
||||
}))
|
||||
|
||||
const config = {
|
||||
series: dataWithColors,
|
||||
xAxis: {
|
||||
categories: data?.years,
|
||||
},
|
||||
chart: {
|
||||
type: 'column',
|
||||
height: '450px',
|
||||
},
|
||||
plotOptions: {
|
||||
column: {
|
||||
stacking: 'percent' as const,
|
||||
dataLabels: {
|
||||
enabled: true,
|
||||
format: '{point.percentage:.1f}%',
|
||||
},
|
||||
},
|
||||
},
|
||||
exporting: {
|
||||
filename: `oodikone_progress_of_students_of_the_studyprogramme_${track}_by_starting_year`,
|
||||
},
|
||||
yAxis: {
|
||||
title: {
|
||||
text: '',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return <ReactHighcharts config={config} />
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
import { Table } from 'semantic-ui-react'
|
||||
|
||||
interface BasicDataTableProps {
|
||||
data: Record<string, Array<Array<string | number>>>
|
||||
titles: string[]
|
||||
track: string
|
||||
}
|
||||
|
||||
export const BasicDataTable = ({ data, titles, track }: BasicDataTableProps) => {
|
||||
if (!data?.[track]?.length || !titles) {
|
||||
return null
|
||||
}
|
||||
|
||||
const sortedData = data[track].toSorted((a, b) => {
|
||||
if (a[0] === 'Total') return 1
|
||||
if (b[0] === 'Total') return -1
|
||||
return parseInt(String(b[0]).split(' - ')[0], 10) - parseInt(String(a[0]).split(' - ')[0], 10)
|
||||
})
|
||||
|
||||
return (
|
||||
<Table celled compact data-cy="study-track-progress-basic-data-table">
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
{titles?.map(title => (
|
||||
<Table.HeaderCell key={title} textAlign="center">
|
||||
{title}
|
||||
</Table.HeaderCell>
|
||||
))}
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{sortedData.map(row => (
|
||||
<Table.Row key={row[0]}>
|
||||
{row.map((value, index) => (
|
||||
<Table.Cell
|
||||
className={row[0] === 'Total' ? 'total-row-cell' : ''}
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
key={`${index}-${value}`}
|
||||
textAlign={index === 0 ? 'center' : 'right'}
|
||||
>
|
||||
{value}
|
||||
</Table.Cell>
|
||||
))}
|
||||
</Table.Row>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
}
|
||||
+45
-51
@@ -1,64 +1,58 @@
|
||||
import { BarChart } from './BarChart'
|
||||
import { BasicDataTable } from './BasicDataTable'
|
||||
import { Stack, Typography } from '@mui/material'
|
||||
|
||||
interface ProgressStats {
|
||||
chartStats: Array<{ data: number[]; name: string }>
|
||||
tableStats: Array<Array<string | number>>
|
||||
import { ProgressBarChart } from '@/components/material/ProgressBarChart'
|
||||
import { ProgressTable } from './ProgressTable'
|
||||
|
||||
type ProgressStats = {
|
||||
chartStats: {
|
||||
data: number[]
|
||||
name: string
|
||||
}[]
|
||||
tableStats: (string | number)[][]
|
||||
tableTitles: string[]
|
||||
}
|
||||
|
||||
interface ProgressOfStudentsProps {
|
||||
progressStats: ProgressStats
|
||||
progressComboStats: ProgressStats
|
||||
track: string
|
||||
export const ProgressOfStudents = ({
|
||||
progressComboStats,
|
||||
progressStats,
|
||||
studyProgramme,
|
||||
years,
|
||||
}: {
|
||||
progressComboStats: ProgressStats | null
|
||||
progressStats: ProgressStats | null
|
||||
studyProgramme: string
|
||||
years: string[]
|
||||
}
|
||||
|
||||
export const ProgressOfStudents = ({ progressStats, progressComboStats, track, years }: ProgressOfStudentsProps) => {
|
||||
}) => {
|
||||
return (
|
||||
<div style={{ marginBottom: '4rem', textAlign: 'center' }}>
|
||||
{progressComboStats != null && (
|
||||
<>
|
||||
<h2 style={{ margin: '0 0 1rem 0' }}>Bachelor + master study right</h2>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '2rem', flexWrap: 'wrap' }}>
|
||||
<div data-cy="study-track-progress-combo-bar-chart" style={{ flex: '2 1 600px', minWidth: '600px' }}>
|
||||
<BarChart
|
||||
data={{
|
||||
creditGraphStats: { [track]: progressComboStats.chartStats },
|
||||
years,
|
||||
}}
|
||||
track={track}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ flex: '1 2 1000px', minWidth: '1000px' }}>
|
||||
<BasicDataTable
|
||||
data={{ [track]: progressComboStats.tableStats }}
|
||||
titles={progressComboStats.tableTitles}
|
||||
track={track}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{progressComboStats != null && <h2 style={{ margin: '2rem 0 1rem 0' }}>Master study right</h2>}
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '2rem', flexWrap: 'wrap' }}>
|
||||
<div data-cy="study-track-progress-bar-chart" style={{ flex: '2 1 600px', minWidth: '600px' }}>
|
||||
<BarChart
|
||||
<Stack gap={2}>
|
||||
{progressComboStats !== null && (
|
||||
<Stack gap={2}>
|
||||
<Typography variant="h6">Bachelor + master study right</Typography>
|
||||
<ProgressBarChart
|
||||
cypress="programme-bachelor-masters"
|
||||
data={{
|
||||
creditGraphStats: { [track]: progressStats.chartStats },
|
||||
id: studyProgramme,
|
||||
stats: progressComboStats.chartStats,
|
||||
years,
|
||||
}}
|
||||
track={track}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ flex: '1 2 1000px', minWidth: '1000px' }}>
|
||||
<BasicDataTable
|
||||
data={{ [track]: progressStats.tableStats }}
|
||||
titles={progressStats.tableTitles}
|
||||
track={track}
|
||||
<ProgressTable data={progressComboStats.tableStats} titles={progressComboStats.tableTitles} />
|
||||
</Stack>
|
||||
)}
|
||||
{progressStats !== null && (
|
||||
<Stack gap={2}>
|
||||
{progressComboStats != null && <Typography variant="h6">Master study right</Typography>}
|
||||
<ProgressBarChart
|
||||
cypress="programme"
|
||||
data={{
|
||||
id: studyProgramme,
|
||||
stats: progressStats.chartStats,
|
||||
years,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ProgressTable data={progressStats.tableStats} titles={progressStats.tableTitles} />
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
import { Paper, TableBody, TableCell, TableContainer, TableHead, TableRow } from '@mui/material'
|
||||
|
||||
import { StyledTable } from '@/components/material/StyledTable'
|
||||
import { getCellKey, getRowKey } from './StudyTrackDataTable/util'
|
||||
|
||||
export const ProgressTable = ({ data, titles }: { data: (string | number)[][]; titles: string[] }) => {
|
||||
if (!data.length || !titles) {
|
||||
return null
|
||||
}
|
||||
|
||||
const sortedData = data.toSorted((a, b) => {
|
||||
if (a[0] === 'Total') return 1
|
||||
if (b[0] === 'Total') return -1
|
||||
return parseInt(String(b[0]).split(' - ')[0], 10) - parseInt(String(a[0]).split(' - ')[0], 10)
|
||||
})
|
||||
|
||||
return (
|
||||
<TableContainer component={Paper} variant="outlined">
|
||||
<StyledTable data-cy="study-programme-progress-table" showCellBorders size="small">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
{titles?.map((title, index) => (
|
||||
<TableCell align="center" key={getCellKey(title, index)}>
|
||||
{title}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{sortedData.map((row, index) => (
|
||||
<TableRow key={getRowKey(row[0].toString(), index)}>
|
||||
{row.map((value, index) => (
|
||||
<TableCell align={index === 0 ? 'center' : 'right'} key={getCellKey(value.toString(), index)}>
|
||||
{value}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</StyledTable>
|
||||
</TableContainer>
|
||||
)
|
||||
}
|
||||
+1
-1
@@ -45,7 +45,7 @@ export const SingleStudyTrackRow = ({
|
||||
)
|
||||
}
|
||||
return (
|
||||
<TableCell align="left" key={getCellKey(code, index)}>
|
||||
<TableCell align="right" key={getCellKey(code, index)}>
|
||||
{value}
|
||||
{index === 0 &&
|
||||
(fullAccessToStudentData ||
|
||||
|
||||
Reference in New Issue
Block a user