Loading frontend/src/components/DefinitionList/index.tsx +17 −5 Original line number Diff line number Diff line Loading @@ -4,7 +4,14 @@ import { css, cx } from '@emotion/css' import type { Definition } from '@inject/graphql' import { GetDefinitions, useTypedQuery } from '@inject/graphql' import type { Column, Row } from '@inject/shared' import { notEmpty, Table, timedFormatter } from '@inject/shared' import { notEmpty, numberSortingFunction, stringSortingFunction, Table, timedFormatter, timestampSortingFunction, } from '@inject/shared' import type { FC } from 'react' import { useMemo } from 'react' import DefinitionButtons from './DefinitionButtons' Loading Loading @@ -41,33 +48,38 @@ const DefinitionList: FC<DefinitionListProps> = ({ className }) => { style: { textAlign: 'left' }, renderValue: definition => definition.name, className: verticallyCentered, sortingFunction: (a, b) => stringSortingFunction(a.name, b.name), }, { id: 'version', name: 'Version', style: { textAlign: 'right', width: '10ch' }, style: { textAlign: 'right', width: '12ch' }, renderValue: definition => definition.version, className: verticallyCentered, sortingFunction: (a, b) => stringSortingFunction(a.version, b.version), }, { id: 'roles', name: 'Roles', style: { textAlign: 'right', width: '10ch' }, style: { textAlign: 'right', width: '12ch' }, renderValue: definition => definition.roles.length || <Cross color={Colors.RED3} />, className: cx(verticallyCentered, hideOnSmallScreen), sortingFunction: (a, b) => numberSortingFunction(a.roles.length, b.roles.length), }, { id: 'uploaded-at', name: 'Uploaded', style: { textAlign: 'right', width: '12ch' }, style: { textAlign: 'right', width: '14ch' }, renderValue: definition => timedFormatter({ datetime: new Date(definition.timestampCreated), minimal: true, }), className: verticallyCentered, sortingFunction: (a, b) => timestampSortingFunction(a.timestampCreated, b.timestampCreated), }, { id: 'actions', Loading frontend/src/components/ExerciseList/index.tsx +32 −2 Original line number Diff line number Diff line Loading @@ -4,7 +4,12 @@ import { css, cx } from '@emotion/css' import type { Exercise } from '@inject/graphql' import { useExercisesSubscription } from '@inject/graphql' import type { Column, Row } from '@inject/shared' import { Table } from '@inject/shared' import { exerciseNameSortingFunction, numberSortingFunction, stringSortingFunction, Table, } from '@inject/shared' import type { NavigateOptions } from '@tanstack/react-router' import type { FC } from 'react' import { useMemo } from 'react' Loading @@ -12,6 +17,25 @@ import { synchronousExerciseState } from '../../utils' import { ExerciseTags } from '../Tags/ExerciseTags' import { ExerciseButtons } from './ExerciseButtons' const exerciseTagsSortingFunction = (a: Exercise, b: Exercise) => { // sort alphabetically const statusOrder: Record<string, number> = { FINISHED: 0, NOT_STARTED: 1, ON_DEMAND: 2, RUNNING: 3, STOPPED: 4, } const getStatusRank = (exercise: Exercise) => { if (exercise.onDemand) return statusOrder.ON_DEMAND const status = synchronousExerciseState(exercise).status return statusOrder[status] ?? Number.MAX_SAFE_INTEGER } return getStatusRank(a) - getStatusRank(b) } const verticallyCentered = css` vertical-align: middle; ` Loading Loading @@ -76,6 +100,7 @@ export const ExerciseList: FC< style: { textAlign: 'left', width: '100%' }, renderValue: exercise => exercise.name, className: verticallyCentered, sortingFunction: (a, b) => exerciseNameSortingFunction(a.name, b.name), }, { id: 'definition', Loading @@ -84,13 +109,17 @@ export const ExerciseList: FC< renderValue: exercise => exercise.definition?.name || exercise.definition?.id, className: verticallyCentered, sortingFunction: (a, b) => stringSortingFunction(a.definition.name, b.definition.name), }, { id: 'teams', name: 'Teams', style: { textAlign: 'right', width: '8ch' }, style: { textAlign: 'right', width: '10ch' }, renderValue: exercise => exercise.teams.length, className: cx(verticallyCentered, hideOnSmallScreen), sortingFunction: (a, b) => numberSortingFunction(a.teams.length, b.teams.length), }, { id: 'status', Loading @@ -103,6 +132,7 @@ export const ExerciseList: FC< /> ), className: verticallyCentered, sortingFunction: exerciseTagsSortingFunction, }, { id: 'actions', Loading shared/components/Table/utils.ts +11 −3 Original line number Diff line number Diff line Loading @@ -8,10 +8,12 @@ export const stringSortingFunction = (a: string, b: string) => * The purpose of this function is to sort team names in a way that * "team-2" comes before "team-10", which is not the case with the * default string sorting function. * * Applies to teams, exercises, ... */ export const teamNameSortingFunction = (a: string, b: string) => { // If custom team name is used, sort as a regular string if (!a.startsWith('team-') || !b.startsWith('team-')) { export const prefixSortingFunction = (a: string, b: string, prefix: string) => { // If custom name is used, sort as a regular string if (!a.startsWith(prefix) || !b.startsWith(prefix)) { return stringSortingFunction(a, b) } const aNumber = parseInt(a.slice(5), 10) Loading @@ -22,6 +24,12 @@ export const teamNameSortingFunction = (a: string, b: string) => { return aNumber - bNumber } export const teamNameSortingFunction = (a: string, b: string) => prefixSortingFunction(a, b, 'team-') export const exerciseNameSortingFunction = (a: string, b: string) => prefixSortingFunction(a, b, 'Exercise ') export const booleanSortingFunction = (a: boolean, b: boolean) => Number(b) - Number(a) Loading Loading
frontend/src/components/DefinitionList/index.tsx +17 −5 Original line number Diff line number Diff line Loading @@ -4,7 +4,14 @@ import { css, cx } from '@emotion/css' import type { Definition } from '@inject/graphql' import { GetDefinitions, useTypedQuery } from '@inject/graphql' import type { Column, Row } from '@inject/shared' import { notEmpty, Table, timedFormatter } from '@inject/shared' import { notEmpty, numberSortingFunction, stringSortingFunction, Table, timedFormatter, timestampSortingFunction, } from '@inject/shared' import type { FC } from 'react' import { useMemo } from 'react' import DefinitionButtons from './DefinitionButtons' Loading Loading @@ -41,33 +48,38 @@ const DefinitionList: FC<DefinitionListProps> = ({ className }) => { style: { textAlign: 'left' }, renderValue: definition => definition.name, className: verticallyCentered, sortingFunction: (a, b) => stringSortingFunction(a.name, b.name), }, { id: 'version', name: 'Version', style: { textAlign: 'right', width: '10ch' }, style: { textAlign: 'right', width: '12ch' }, renderValue: definition => definition.version, className: verticallyCentered, sortingFunction: (a, b) => stringSortingFunction(a.version, b.version), }, { id: 'roles', name: 'Roles', style: { textAlign: 'right', width: '10ch' }, style: { textAlign: 'right', width: '12ch' }, renderValue: definition => definition.roles.length || <Cross color={Colors.RED3} />, className: cx(verticallyCentered, hideOnSmallScreen), sortingFunction: (a, b) => numberSortingFunction(a.roles.length, b.roles.length), }, { id: 'uploaded-at', name: 'Uploaded', style: { textAlign: 'right', width: '12ch' }, style: { textAlign: 'right', width: '14ch' }, renderValue: definition => timedFormatter({ datetime: new Date(definition.timestampCreated), minimal: true, }), className: verticallyCentered, sortingFunction: (a, b) => timestampSortingFunction(a.timestampCreated, b.timestampCreated), }, { id: 'actions', Loading
frontend/src/components/ExerciseList/index.tsx +32 −2 Original line number Diff line number Diff line Loading @@ -4,7 +4,12 @@ import { css, cx } from '@emotion/css' import type { Exercise } from '@inject/graphql' import { useExercisesSubscription } from '@inject/graphql' import type { Column, Row } from '@inject/shared' import { Table } from '@inject/shared' import { exerciseNameSortingFunction, numberSortingFunction, stringSortingFunction, Table, } from '@inject/shared' import type { NavigateOptions } from '@tanstack/react-router' import type { FC } from 'react' import { useMemo } from 'react' Loading @@ -12,6 +17,25 @@ import { synchronousExerciseState } from '../../utils' import { ExerciseTags } from '../Tags/ExerciseTags' import { ExerciseButtons } from './ExerciseButtons' const exerciseTagsSortingFunction = (a: Exercise, b: Exercise) => { // sort alphabetically const statusOrder: Record<string, number> = { FINISHED: 0, NOT_STARTED: 1, ON_DEMAND: 2, RUNNING: 3, STOPPED: 4, } const getStatusRank = (exercise: Exercise) => { if (exercise.onDemand) return statusOrder.ON_DEMAND const status = synchronousExerciseState(exercise).status return statusOrder[status] ?? Number.MAX_SAFE_INTEGER } return getStatusRank(a) - getStatusRank(b) } const verticallyCentered = css` vertical-align: middle; ` Loading Loading @@ -76,6 +100,7 @@ export const ExerciseList: FC< style: { textAlign: 'left', width: '100%' }, renderValue: exercise => exercise.name, className: verticallyCentered, sortingFunction: (a, b) => exerciseNameSortingFunction(a.name, b.name), }, { id: 'definition', Loading @@ -84,13 +109,17 @@ export const ExerciseList: FC< renderValue: exercise => exercise.definition?.name || exercise.definition?.id, className: verticallyCentered, sortingFunction: (a, b) => stringSortingFunction(a.definition.name, b.definition.name), }, { id: 'teams', name: 'Teams', style: { textAlign: 'right', width: '8ch' }, style: { textAlign: 'right', width: '10ch' }, renderValue: exercise => exercise.teams.length, className: cx(verticallyCentered, hideOnSmallScreen), sortingFunction: (a, b) => numberSortingFunction(a.teams.length, b.teams.length), }, { id: 'status', Loading @@ -103,6 +132,7 @@ export const ExerciseList: FC< /> ), className: verticallyCentered, sortingFunction: exerciseTagsSortingFunction, }, { id: 'actions', Loading
shared/components/Table/utils.ts +11 −3 Original line number Diff line number Diff line Loading @@ -8,10 +8,12 @@ export const stringSortingFunction = (a: string, b: string) => * The purpose of this function is to sort team names in a way that * "team-2" comes before "team-10", which is not the case with the * default string sorting function. * * Applies to teams, exercises, ... */ export const teamNameSortingFunction = (a: string, b: string) => { // If custom team name is used, sort as a regular string if (!a.startsWith('team-') || !b.startsWith('team-')) { export const prefixSortingFunction = (a: string, b: string, prefix: string) => { // If custom name is used, sort as a regular string if (!a.startsWith(prefix) || !b.startsWith(prefix)) { return stringSortingFunction(a, b) } const aNumber = parseInt(a.slice(5), 10) Loading @@ -22,6 +24,12 @@ export const teamNameSortingFunction = (a: string, b: string) => { return aNumber - bNumber } export const teamNameSortingFunction = (a: string, b: string) => prefixSortingFunction(a, b, 'team-') export const exerciseNameSortingFunction = (a: string, b: string) => prefixSortingFunction(a, b, 'Exercise ') export const booleanSortingFunction = (a: boolean, b: boolean) => Number(b) - Number(a) Loading