diff --git a/.vscode/settings.json b/.vscode/settings.json index 0ed37ce2..34febc99 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,7 +9,11 @@ "files.autoSave": "onFocusChange", // ESLint - "eslint.alwaysShowStatus": true, + "eslint.experimental.useFlatConfig": true, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + }, + "eslint.validate": ["javascript"], // Prettier "prettier.semi": false, diff --git a/apps/server/routers/chartTypes.ts b/apps/server/routers/chartTypes.ts index 74d7694a..8bd02b92 100644 --- a/apps/server/routers/chartTypes.ts +++ b/apps/server/routers/chartTypes.ts @@ -16,6 +16,7 @@ export type ChartGroup = { export type SingularChartData = | BarChartData + | TProfileChartData | RadarChartData | LineChartData | PieChartData @@ -29,6 +30,14 @@ export interface BarChartData { maxValue?: number | string } +export interface TProfileChartData { + type: 'TProfileChart' + indexBy: string + keys: string[] + data: any[] + maxValue?: number | string +} + export interface RadarChartData { type: 'RadarChart' indexBy: string diff --git a/apps/server/routers/employees/employeeChartConversion.ts b/apps/server/routers/employees/employeeChartConversion.ts index 5298a49c..11761763 100644 --- a/apps/server/routers/employees/employeeChartConversion.ts +++ b/apps/server/routers/employees/employeeChartConversion.ts @@ -1,7 +1,13 @@ -import { MultipleChartData, BarChartData, RadarChartData } from '../chartTypes' +import { + MultipleChartData, + BarChartData, + RadarChartData, + TProfileChartData, +} from '../chartTypes' import { aggregateEmployeeCompetenceAndMotivation } from './employeesAggregation' import { + CompetenceScore, EmployeeCompetenceScore, EmployeeMotivationAndCompetence, } from './employeesTypes' @@ -47,35 +53,27 @@ export const employeeMotivationAndCompetence = ( export const employeeCompetenceScore = ( data: EmployeeCompetenceScore[] -): MultipleChartData<[BarChartData, RadarChartData]> => { +): TProfileChartData => { const indexBy = 'category' const keys = ['score'] - const name = 'T-formet' const maxScore = Math.max(...data.map((s) => s.score)) + const dataSubset = data.map((e) => mapCompetenceScore(e)) return { - type: 'MultipleChart', - groups: [ - { - name, - charts: [ - { - type: 'BarChart', - indexBy, - keys, - data, - maxValue: maxScore, - }, - { - type: 'RadarChart', - indexBy, - keys, - data, - maxValue: maxScore, - }, - ], - }, - ], + type: 'TProfileChart', + indexBy, + keys, + data: dataSubset, + maxValue: maxScore, + } +} + +function mapCompetenceScore( + employeeCompetenceScore: EmployeeCompetenceScore +): CompetenceScore { + return { + category: employeeCompetenceScore.category, + score: employeeCompetenceScore.score, } } diff --git a/apps/server/routers/employees/employeesRouter.ts b/apps/server/routers/employees/employeesRouter.ts index afd7bbb8..f1afa6cc 100644 --- a/apps/server/routers/employees/employeesRouter.ts +++ b/apps/server/routers/employees/employeesRouter.ts @@ -12,6 +12,7 @@ import { } from './employeesAggregation' import { getFileFromS3 } from '../../dataplattform/databricksS3Call' +import { EmployeeCompetenceScore } from './employeesTypes' const router: Router = express.Router() @@ -134,11 +135,11 @@ router.get( const employeeCompetenceSc = await getFileFromS3( 'employeeCompetenceScore' ) - let data = JSON.parse(employeeCompetenceSc) - data = data + const data: EmployeeCompetenceScore[] = JSON.parse(employeeCompetenceSc) + const filtered = data .filter((i) => i.email == req.query.email) .sort((e1, e2) => e1.sorting - e2.sorting) - const aggregatedData = employeeCompetenceScore(data) + const aggregatedData = employeeCompetenceScore(filtered) res.send(aggregatedData) } catch (error) { next(error) diff --git a/apps/server/routers/employees/employeesTypes.ts b/apps/server/routers/employees/employeesTypes.ts index eb8b5c34..9033f73c 100644 --- a/apps/server/routers/employees/employeesTypes.ts +++ b/apps/server/routers/employees/employeesTypes.ts @@ -76,6 +76,10 @@ export type EmployeeCompetenceScore = { score: number sorting: number } +export type CompetenceScore = { + category: string + score: number +} export type EmployeeSkillsReport = EmployeeSkills[] export type EmployeeSkills = { diff --git a/apps/web/src/components/charts/ChartCard.tsx b/apps/web/src/components/charts/ChartCard.tsx index 35324f36..062e4f70 100644 --- a/apps/web/src/components/charts/ChartCard.tsx +++ b/apps/web/src/components/charts/ChartCard.tsx @@ -26,10 +26,12 @@ export const SingularChart = ({ isBig, chartData, ...props -}: SingularChartProps & IsBigProps) => { +}: SingularChartProps & IsBigProps): React.ReactNode => { switch (chartData.type) { case 'BarChart': return + case 'TProfileChart': + return case 'RadarChart': return case 'LineChart': @@ -61,7 +63,7 @@ const ChartCard = ({ title, legendWidth, ...props -}: ChartCardProps) => { +}: ChartCardProps): React.ReactNode => { if (error) return ( diff --git a/apps/web/src/components/charts/ChartDisplayOptions.tsx b/apps/web/src/components/charts/ChartDisplayOptions.tsx index 8d640a90..bdd97e7c 100644 --- a/apps/web/src/components/charts/ChartDisplayOptions.tsx +++ b/apps/web/src/components/charts/ChartDisplayOptions.tsx @@ -1,6 +1,12 @@ import React from 'react' import { ToggleButton, ToggleButtonGroup } from '@mui/material' -import { BarChart, PieChart, ShowChart, DonutLarge } from '@mui/icons-material' +import { + BarChart, + PieChart, + ShowChart, + DonutLarge, + TextFields, +} from '@mui/icons-material' import { styled } from '@mui/material/styles' import { SvgIcon } from '@mui/material' import RadarLogo from '../../assets/RadarChart.svg' @@ -16,6 +22,7 @@ const chartVariantInfo: { } = { LineChart: { label: 'linjediagram', icon: }, BarChart: { label: 'stolpediagram', icon: }, + TProfileChart: { label: 'tprofildiagram', icon: }, PieChart: { label: 'kakediagram', icon: }, RadarChart: { label: 'radardiagram', @@ -43,13 +50,13 @@ export function ChartVariantToggle({ onChange, big = false, title, -}: ChartVariantToggleProps) { +}: ChartVariantToggleProps): React.ReactNode { const { trackEvent } = useMatomo() const handleChartVariantChange = ( event: React.MouseEvent, newChartIndex: number | null - ) => { + ): void => { if (typeof newChartIndex === 'number') { onChange(newChartIndex) } @@ -66,7 +73,7 @@ export function ChartVariantToggle({ const { label, icon: ChartIcon } = chartVariantInfo[chartVariant.type] const buttonLabel = `Vis som ${label}` - const chartOptionChange = (label: string) => { + const chartOptionChange = (label: string): void => { trackEvent({ category: 'Graph type', action: 'Changed graph type', @@ -102,6 +109,8 @@ interface ChartDisplayOptionsProps { children: React.ReactNode | React.ReactNode[] } -export function ChartDisplayOptions({ children }: ChartDisplayOptionsProps) { +export function ChartDisplayOptions({ + children, +}: ChartDisplayOptionsProps): React.ReactNode { return {children} } diff --git a/apps/web/src/components/charts/DropdownPicker.tsx b/apps/web/src/components/charts/DropdownPicker.tsx index bc3f6b27..d1049014 100644 --- a/apps/web/src/components/charts/DropdownPicker.tsx +++ b/apps/web/src/components/charts/DropdownPicker.tsx @@ -69,7 +69,7 @@ export default function DropdownPicker({ selected = '', title, big, -}: DropdownPickerProps) { +}: DropdownPickerProps): React.ReactNode { const width = measureTextWidth( values diff --git a/apps/web/src/components/charts/MultipleChartCard.tsx b/apps/web/src/components/charts/MultipleChartCard.tsx index 24347914..b47cb417 100644 --- a/apps/web/src/components/charts/MultipleChartCard.tsx +++ b/apps/web/src/components/charts/MultipleChartCard.tsx @@ -36,13 +36,21 @@ const MultipleChartCard = ({ fullSize, data: { groups }, noDataText, -}: MultipleChartCardProps) => { +}: MultipleChartCardProps): React.ReactNode => { const [selectedGroupName, setSelectedGroupName] = useState(groups[0].name) const [selectedChartIndex, setSelectedChartIndex] = useState(0) const [isBig, setIsBig] = useState(false) const selectedGroup = groups.find((group) => group.name === selectedGroupName) + const onChangeSelectedGroup = (value: string): void => { + const group = groups.find((g) => g.name == value) + if (group.charts.length >= selectedChartIndex) { + setSelectedChartIndex(0) + } + setSelectedGroupName(value) + } + return ( {/* Header containing dropdown picker */} @@ -50,7 +58,7 @@ const MultipleChartCard = ({ group.name)} selected={selectedGroupName} - onChange={setSelectedGroupName} + onChange={onChangeSelectedGroup} title={title} /> diff --git a/apps/web/src/pages/employee/cards/EmployeeCompetenceScoreCard.tsx b/apps/web/src/pages/employee/cards/EmployeeCompetenceScoreCard.tsx deleted file mode 100644 index 2468f525..00000000 --- a/apps/web/src/pages/employee/cards/EmployeeCompetenceScoreCard.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React, { FC } from 'react' -import { useEmployeeCompetenceScoreCharts } from '../../../api/data/employee/employeeQueries' -import ChartCard from '../../../components/charts/ChartCard' - -interface Props { - employeeEmail: string -} - -const EmployeeCompetenceScoreCard: FC = ({ employeeEmail }) => { - const { data, error } = useEmployeeCompetenceScoreCharts(employeeEmail) - return ( - - ) -} - -export default EmployeeCompetenceScoreCard diff --git a/apps/web/src/pages/employee/cards/EmployeeMotivationAndCompetenceCard.tsx b/apps/web/src/pages/employee/cards/EmployeeMotivationAndCompetenceCard.tsx index 2b639c9a..7b0ad1ba 100644 --- a/apps/web/src/pages/employee/cards/EmployeeMotivationAndCompetenceCard.tsx +++ b/apps/web/src/pages/employee/cards/EmployeeMotivationAndCompetenceCard.tsx @@ -1,20 +1,55 @@ -import React, { FC } from 'react' -import { useEmployeeMotivationAndCompetenceCharts } from '../../../api/data/employee/employeeQueries' +import { FC } from 'react' +import { + useEmployeeCompetenceScoreCharts, + useEmployeeMotivationAndCompetenceCharts, +} from '../../../api/data/employee/employeeQueries' import ChartCard from '../../../components/charts/ChartCard' +import { + MultipleChartData, + SingularChartData, + TProfileChartData, +} from '@folk/common/types/chartTypes' interface Props { employeeEmail: string } const EmployeeCompetenceCard: FC = ({ employeeEmail }) => { - const { data, error } = + const { data: motAndCompData, error: motAndCompError } = useEmployeeMotivationAndCompetenceCharts(employeeEmail) + const { data: scoreData, error: scoreError } = + useEmployeeCompetenceScoreCharts(employeeEmail) + + // Append t-profile chart, unless it exists, to the first chart group ("Hovedkategorier") in motAndCompData. + const appendTProfileChartIfNotExists = (motAndCompData, scoreData): void => { + const isTProfileChartData = ( + value: SingularChartData + ): value is SingularChartData => value?.type === 'TProfileChart' + + if (motAndCompData && scoreData) { + //if (!motAndCompError) { + if ( + !( + motAndCompData as MultipleChartData + ).groups[0].charts.some((e) => isTProfileChartData(e)) + ) { + if (!scoreError) { + ;( + motAndCompData as MultipleChartData + ).groups[0].charts.push(scoreData as TProfileChartData) + } + } + } + } + + appendTProfileChartIfNotExists(motAndCompData, scoreData) + return ( ) } diff --git a/apps/web/src/pages/employee/components/EmployeeProfileContent.tsx b/apps/web/src/pages/employee/components/EmployeeProfileContent.tsx index a8db2bae..93bd2365 100644 --- a/apps/web/src/pages/employee/components/EmployeeProfileContent.tsx +++ b/apps/web/src/pages/employee/components/EmployeeProfileContent.tsx @@ -11,7 +11,6 @@ import { EmployeeNotFound } from './EmployeeNotFound' import { FallbackMessage } from './FallbackMessage' import { ProjectExperienceList } from './ProjectExperienceList' import { WorkExperienceList } from './WorkExperienceList' -import EmployeeCompetenceScoreCard from '../cards/EmployeeCompetenceScoreCard' const ComponentRoot = styled('article')(() => ({ display: 'flex', @@ -104,7 +103,6 @@ export function EmployeeProfileContent({ employeeEmail }: Props) { - diff --git a/apps/web/src/pages/employee/table/EmployeeTableExpandedInfo.tsx b/apps/web/src/pages/employee/table/EmployeeTableExpandedInfo.tsx index c6ecb691..8fcbba87 100644 --- a/apps/web/src/pages/employee/table/EmployeeTableExpandedInfo.tsx +++ b/apps/web/src/pages/employee/table/EmployeeTableExpandedInfo.tsx @@ -5,7 +5,6 @@ import { CompetenceSummary } from '../components/CompetenceSummary' import EmployeeCompetenceCard from '../cards/EmployeeMotivationAndCompetenceCard' import { ProjectExperienceList } from '../components/ProjectExperienceList' import { WorkExperienceList } from '../components/WorkExperienceList' -import EmployeeCompetenceScoreCard from '../cards/EmployeeCompetenceScoreCard' const ComponentRoot = styled('div')(({ theme }) => ({ display: 'flex', @@ -78,7 +77,6 @@ export function EmployeeTableExpandedInfo({ data }: Props) { - ) diff --git a/packages/folk-common/types/chartTypes.ts b/packages/folk-common/types/chartTypes.ts index 76b12032..9a4e6e77 100644 --- a/packages/folk-common/types/chartTypes.ts +++ b/packages/folk-common/types/chartTypes.ts @@ -16,6 +16,7 @@ export type ChartGroup = { export type SingularChartData = | BarChartData + | TProfileChartData | RadarChartData | LineChartData | PieChartData @@ -29,6 +30,15 @@ export interface BarChartData { weeklyData?: LineChartData; } +// A variant of BarChart, but needs a separate type name so that we can set a different icon on the toggle button +export interface TProfileChartData { + type: "TProfileChart"; + indexBy: string; + keys: string[]; + data: any[]; + weeklyData?: LineChartData; +} + export interface RadarChartData { type: "RadarChart"; indexBy: string;