Loading frontend/src/components/ExerciseList/ExerciseButtons/InfoButton/SandboxInfo.tsx +35 −9 Original line number Diff line number Diff line import { Button, Collapse } from '@blueprintjs/core' import { Button, ButtonGroup, Collapse } from '@blueprintjs/core' import { css } from '@emotion/css' import type { TeamTokenF } from '@inject/graphql' import { ExerciseTokensQuery, useTypedQuery } from '@inject/graphql' Loading Loading @@ -58,7 +58,7 @@ export const SandboxInfo: FC<ConfigInfoProps> = ({ { id: 'token', name: t('exerciseInfo.token'), style: { width: '60%' }, style: { width: '60%', whiteSpace: 'pre-wrap', wordBreak: 'break-word' }, renderValue: teamToken => teamToken.token, className: verticallyCentered, sortingFunction: (a, b) => stringSortingFunction(a.token, b.token), Loading @@ -71,8 +71,30 @@ export const SandboxInfo: FC<ConfigInfoProps> = ({ value: token, })) || [] const exportAsCSV = () => { const header = ['teamIds', 'username', 'token'].join(',') const csvRows = data?.exerciseTokens.map(token => { const teamIds = `"${token.teamIds.join('|')}"` const username = `"${token.user.username}"` const tokenValue = `"${token.token}"` return [teamIds, username, tokenValue].join(',') }) || [] const csvContent = [header, ...csvRows].join('\n') const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }) const link = document.createElement('a') const url = URL.createObjectURL(blob) link.setAttribute('href', url) link.setAttribute('download', `exercise_${exerciseId}_tokens.csv`) link.style.visibility = 'hidden' document.body.appendChild(link) link.click() document.body.removeChild(link) } return ( <div className={className}> <ButtonGroup> <Button onClick={() => setOpen(!open)} active={open} loading={fetching}> {`${ open Loading @@ -80,6 +102,10 @@ export const SandboxInfo: FC<ConfigInfoProps> = ({ : t('exercisePanel.definitionManager.info.showTokens') } ${t('exercisePanel.definitionManager.info.tokens')}`} </Button> <Button onClick={exportAsCSV}> {t('exercisePanel.definitionManager.info.exportTokens')} </Button> </ButtonGroup> <Collapse isOpen={open}> <Table<TeamTokenF> columns={columns} Loading frontend/src/routes/_protected/_navbar/users/index.tsx +0 −11 Original line number Diff line number Diff line import { Navbar } from '@blueprintjs/core' import { css } from '@emotion/css' import type { IUser } from '@inject/graphql' import { Loading Loading @@ -200,16 +199,6 @@ const RouteComponent = () => { link={{ to: ExercisePanelRoute.to }} button={{ text: 'Exercise Panel', icon: 'panel', minimal: true }} /> <Navbar.Divider className={css` align-self: center; display: none; @media (min-width: 75rem) { display: block; } `} /> </PrependNavbar> <Sidebar position='right' className={sidebarClass} sections={sections} /> </div> Loading locale/resources/cs/frontend.json +2 −1 Original line number Diff line number Diff line Loading @@ -737,7 +737,8 @@ "showTokens": "Zobrazit", "tokens": "tokeny", "noTokensTitle": "Žádné tokeny", "noTokensDescription": "Žádní uživatelé nejsou k tomuto cvičení přiřazeni" "noTokensDescription": "Žádní uživatelé nejsou k tomuto cvičení přiřazeni", "exportTokens": "Exportovat tokeny do CSV" } }, "exportImport": { Loading locale/resources/en/frontend.json +2 −1 Original line number Diff line number Diff line Loading @@ -737,7 +737,8 @@ "showTokens": "Show", "tokens": "tokens", "noTokensTitle": "No tokens", "noTokensDescription": "No users are assigned to this exercise yet" "noTokensDescription": "No users are assigned to this exercise yet", "exportTokens": "Export tokens as CSV" } }, "exportImport": { Loading Loading
frontend/src/components/ExerciseList/ExerciseButtons/InfoButton/SandboxInfo.tsx +35 −9 Original line number Diff line number Diff line import { Button, Collapse } from '@blueprintjs/core' import { Button, ButtonGroup, Collapse } from '@blueprintjs/core' import { css } from '@emotion/css' import type { TeamTokenF } from '@inject/graphql' import { ExerciseTokensQuery, useTypedQuery } from '@inject/graphql' Loading Loading @@ -58,7 +58,7 @@ export const SandboxInfo: FC<ConfigInfoProps> = ({ { id: 'token', name: t('exerciseInfo.token'), style: { width: '60%' }, style: { width: '60%', whiteSpace: 'pre-wrap', wordBreak: 'break-word' }, renderValue: teamToken => teamToken.token, className: verticallyCentered, sortingFunction: (a, b) => stringSortingFunction(a.token, b.token), Loading @@ -71,8 +71,30 @@ export const SandboxInfo: FC<ConfigInfoProps> = ({ value: token, })) || [] const exportAsCSV = () => { const header = ['teamIds', 'username', 'token'].join(',') const csvRows = data?.exerciseTokens.map(token => { const teamIds = `"${token.teamIds.join('|')}"` const username = `"${token.user.username}"` const tokenValue = `"${token.token}"` return [teamIds, username, tokenValue].join(',') }) || [] const csvContent = [header, ...csvRows].join('\n') const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }) const link = document.createElement('a') const url = URL.createObjectURL(blob) link.setAttribute('href', url) link.setAttribute('download', `exercise_${exerciseId}_tokens.csv`) link.style.visibility = 'hidden' document.body.appendChild(link) link.click() document.body.removeChild(link) } return ( <div className={className}> <ButtonGroup> <Button onClick={() => setOpen(!open)} active={open} loading={fetching}> {`${ open Loading @@ -80,6 +102,10 @@ export const SandboxInfo: FC<ConfigInfoProps> = ({ : t('exercisePanel.definitionManager.info.showTokens') } ${t('exercisePanel.definitionManager.info.tokens')}`} </Button> <Button onClick={exportAsCSV}> {t('exercisePanel.definitionManager.info.exportTokens')} </Button> </ButtonGroup> <Collapse isOpen={open}> <Table<TeamTokenF> columns={columns} Loading
frontend/src/routes/_protected/_navbar/users/index.tsx +0 −11 Original line number Diff line number Diff line import { Navbar } from '@blueprintjs/core' import { css } from '@emotion/css' import type { IUser } from '@inject/graphql' import { Loading Loading @@ -200,16 +199,6 @@ const RouteComponent = () => { link={{ to: ExercisePanelRoute.to }} button={{ text: 'Exercise Panel', icon: 'panel', minimal: true }} /> <Navbar.Divider className={css` align-self: center; display: none; @media (min-width: 75rem) { display: block; } `} /> </PrependNavbar> <Sidebar position='right' className={sidebarClass} sections={sections} /> </div> Loading
locale/resources/cs/frontend.json +2 −1 Original line number Diff line number Diff line Loading @@ -737,7 +737,8 @@ "showTokens": "Zobrazit", "tokens": "tokeny", "noTokensTitle": "Žádné tokeny", "noTokensDescription": "Žádní uživatelé nejsou k tomuto cvičení přiřazeni" "noTokensDescription": "Žádní uživatelé nejsou k tomuto cvičení přiřazeni", "exportTokens": "Exportovat tokeny do CSV" } }, "exportImport": { Loading
locale/resources/en/frontend.json +2 −1 Original line number Diff line number Diff line Loading @@ -737,7 +737,8 @@ "showTokens": "Show", "tokens": "tokens", "noTokensTitle": "No tokens", "noTokensDescription": "No users are assigned to this exercise yet" "noTokensDescription": "No users are assigned to this exercise yet", "exportTokens": "Export tokens as CSV" } }, "exportImport": { Loading