Loading backend @ e5a8728b Compare 2ee23f62 to e5a8728b Original line number Diff line number Diff line Subproject commit 2ee23f62b1883ee941f17f4192426ba4c589d884 Subproject commit e5a8728b30d506aec9c326673c077d1f4b98fc91 codegen/package.json +1 −1 Original line number Diff line number Diff line { "name": "@inject/codegen", "version": "3.13.0", "version": "3.14.0", "description": "GraphQL API Codegen Setup for the Inject Backend", "main": "index.js", "license": "MIT", Loading frontend/package.json +1 −1 Original line number Diff line number Diff line { "name": "@inject/frontend", "version": "3.13.0", "version": "3.14.0", "description": "Main wrapper for rendering INJECT Frontend", "main": "index.js", "license": "MIT", Loading frontend/src/components/Drive/DriveTable.tsx 0 → 100644 +124 −0 Original line number Diff line number Diff line import { Button } from '@blueprintjs/core' import { css } from '@emotion/css' import type { FileInfo } from '@inject/graphql' import type { Column } from '@inject/shared' import { dateSortingFunction, stringSortingFunction, Table, timedFormatter, } from '@inject/shared' import type { NavigateOptions } from '@tanstack/react-router' import { useNavigate } from '@tanstack/react-router' import type { FC } from 'react' import { useMemo } from 'react' type DriveProps = { fileInfos: FileInfo[] searchString: string getFileLink: (fileId: string) => NavigateOptions } const verticallyCentered = css` vertical-align: middle; ` const DriveTable: FC<DriveProps> = ({ fileInfos, searchString, getFileLink, }) => { const nav = useNavigate() const handleClick = ( e: React.MouseEvent<HTMLAnchorElement, MouseEvent>, link: NavigateOptions ) => { e.preventDefault() nav({ ...link, state: { backButtonProps: { button: { text: 'Back', minimal: true, icon: 'chevron-left' }, back: true, }, }, }) } const columns: Column<FileInfo>[] = [ { id: 'name', name: 'Name', style: { textAlign: 'left' }, renderValue: file => file.fileName, sortingFunction: (a, b) => stringSortingFunction(a.fileName, b.fileName), className: verticallyCentered, }, { id: 'uploaded', name: 'Uploaded', style: { textAlign: 'left', width: '20ch' }, renderValue: file => timedFormatter({ datetime: new Date(file.uploadedAt) }), sortingFunction: (a, b) => dateSortingFunction(new Date(a.uploadedAt), new Date(b.uploadedAt)), className: verticallyCentered, }, { id: 'actions', name: 'Actions', style: { textAlign: 'right', width: '14ch', }, renderValue: file => { const link = getFileLink(file.id) return ( <a href={link.href} onClick={e => handleClick(e, link)}> <Button icon='eye-open' title='Preview' /> </a> ) }, className: verticallyCentered, }, ] const filteredFiles = useMemo( () => fileInfos.filter(oneFile => oneFile.fileName.toLowerCase().includes(searchString.toLowerCase()) ), [fileInfos, searchString] ) const rows = useMemo( () => filteredFiles .map(fileInfo => ({ id: fileInfo.id, value: fileInfo, })) .sort((a, b) => stringSortingFunction(a.value.fileName, b.value.fileName) ), [filteredFiles] ) return ( <Table<FileInfo> columns={columns} rows={rows} className={css` overflow-y: scroll; `} noDataStateProps={{ title: 'No files', description: 'There are no files to display', }} /> ) } export default DriveTable frontend/src/components/Drive/index.tsx 0 → 100644 +63 −0 Original line number Diff line number Diff line import { NonIdealState } from '@blueprintjs/core' import { css } from '@emotion/css' import { ExerciseDrive, useTypedQuery } from '@inject/graphql' import { CenteredSpinner, Container } from '@inject/shared' import type { NavigateOptions } from '@tanstack/react-router' import type { FC } from 'react' import { useState } from 'react' import SearchInput from '../SearchInput' import DriveTable from './DriveTable' type DriveProps = { exerciseId: string getFileLink: (fileId: string) => NavigateOptions } const Drive: FC<DriveProps> = ({ exerciseId, getFileLink }) => { const [{ fetching: loading, data }] = useTypedQuery({ query: ExerciseDrive, variables: { exerciseId, }, }) const [searchString, setSearchString] = useState('') if (loading) { return <CenteredSpinner /> } if (!data || !data.exerciseDrive) { return ( <NonIdealState icon='floppy-disk' title='No disk' description='There is no disk to display' /> ) } return ( <Container className={css` overflow-y: hidden; display: flex; flex-direction: column; row-gap: 1rem; padding-top: 0.5rem; `} > <SearchInput searchString={searchString} setSearchString={setSearchString} /> <DriveTable fileInfos={data.exerciseDrive} searchString={searchString} getFileLink={getFileLink} /> </Container> ) } export default Drive Loading
backend @ e5a8728b Compare 2ee23f62 to e5a8728b Original line number Diff line number Diff line Subproject commit 2ee23f62b1883ee941f17f4192426ba4c589d884 Subproject commit e5a8728b30d506aec9c326673c077d1f4b98fc91
codegen/package.json +1 −1 Original line number Diff line number Diff line { "name": "@inject/codegen", "version": "3.13.0", "version": "3.14.0", "description": "GraphQL API Codegen Setup for the Inject Backend", "main": "index.js", "license": "MIT", Loading
frontend/package.json +1 −1 Original line number Diff line number Diff line { "name": "@inject/frontend", "version": "3.13.0", "version": "3.14.0", "description": "Main wrapper for rendering INJECT Frontend", "main": "index.js", "license": "MIT", Loading
frontend/src/components/Drive/DriveTable.tsx 0 → 100644 +124 −0 Original line number Diff line number Diff line import { Button } from '@blueprintjs/core' import { css } from '@emotion/css' import type { FileInfo } from '@inject/graphql' import type { Column } from '@inject/shared' import { dateSortingFunction, stringSortingFunction, Table, timedFormatter, } from '@inject/shared' import type { NavigateOptions } from '@tanstack/react-router' import { useNavigate } from '@tanstack/react-router' import type { FC } from 'react' import { useMemo } from 'react' type DriveProps = { fileInfos: FileInfo[] searchString: string getFileLink: (fileId: string) => NavigateOptions } const verticallyCentered = css` vertical-align: middle; ` const DriveTable: FC<DriveProps> = ({ fileInfos, searchString, getFileLink, }) => { const nav = useNavigate() const handleClick = ( e: React.MouseEvent<HTMLAnchorElement, MouseEvent>, link: NavigateOptions ) => { e.preventDefault() nav({ ...link, state: { backButtonProps: { button: { text: 'Back', minimal: true, icon: 'chevron-left' }, back: true, }, }, }) } const columns: Column<FileInfo>[] = [ { id: 'name', name: 'Name', style: { textAlign: 'left' }, renderValue: file => file.fileName, sortingFunction: (a, b) => stringSortingFunction(a.fileName, b.fileName), className: verticallyCentered, }, { id: 'uploaded', name: 'Uploaded', style: { textAlign: 'left', width: '20ch' }, renderValue: file => timedFormatter({ datetime: new Date(file.uploadedAt) }), sortingFunction: (a, b) => dateSortingFunction(new Date(a.uploadedAt), new Date(b.uploadedAt)), className: verticallyCentered, }, { id: 'actions', name: 'Actions', style: { textAlign: 'right', width: '14ch', }, renderValue: file => { const link = getFileLink(file.id) return ( <a href={link.href} onClick={e => handleClick(e, link)}> <Button icon='eye-open' title='Preview' /> </a> ) }, className: verticallyCentered, }, ] const filteredFiles = useMemo( () => fileInfos.filter(oneFile => oneFile.fileName.toLowerCase().includes(searchString.toLowerCase()) ), [fileInfos, searchString] ) const rows = useMemo( () => filteredFiles .map(fileInfo => ({ id: fileInfo.id, value: fileInfo, })) .sort((a, b) => stringSortingFunction(a.value.fileName, b.value.fileName) ), [filteredFiles] ) return ( <Table<FileInfo> columns={columns} rows={rows} className={css` overflow-y: scroll; `} noDataStateProps={{ title: 'No files', description: 'There are no files to display', }} /> ) } export default DriveTable
frontend/src/components/Drive/index.tsx 0 → 100644 +63 −0 Original line number Diff line number Diff line import { NonIdealState } from '@blueprintjs/core' import { css } from '@emotion/css' import { ExerciseDrive, useTypedQuery } from '@inject/graphql' import { CenteredSpinner, Container } from '@inject/shared' import type { NavigateOptions } from '@tanstack/react-router' import type { FC } from 'react' import { useState } from 'react' import SearchInput from '../SearchInput' import DriveTable from './DriveTable' type DriveProps = { exerciseId: string getFileLink: (fileId: string) => NavigateOptions } const Drive: FC<DriveProps> = ({ exerciseId, getFileLink }) => { const [{ fetching: loading, data }] = useTypedQuery({ query: ExerciseDrive, variables: { exerciseId, }, }) const [searchString, setSearchString] = useState('') if (loading) { return <CenteredSpinner /> } if (!data || !data.exerciseDrive) { return ( <NonIdealState icon='floppy-disk' title='No disk' description='There is no disk to display' /> ) } return ( <Container className={css` overflow-y: hidden; display: flex; flex-direction: column; row-gap: 1rem; padding-top: 0.5rem; `} > <SearchInput searchString={searchString} setSearchString={setSearchString} /> <DriveTable fileInfos={data.exerciseDrive} searchString={searchString} getFileLink={getFileLink} /> </Container> ) } export default Drive