Loading frontend/src/actionlog/InjectMessage/DeleteCommentButton.tsx +6 −4 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ import { DeleteInstructorComment, useTypedMutation, } from '@inject/graphql' import { useTranslationFrontend } from '@inject/locale' import { type FC, useState } from 'react' type DeleteCommentButtonProps = { Loading @@ -30,6 +31,7 @@ const DeleteCommentButton: FC<DeleteCommentButtonProps> = ({ const [{ fetching }, deleteComment] = useTypedMutation( DeleteInstructorComment ) const { t } = useTranslationFrontend() const onClick = () => { deleteComment({ commentId, exerciseId }, { Loading Loading @@ -63,23 +65,23 @@ const DeleteCommentButton: FC<DeleteCommentButtonProps> = ({ type='button' icon='trash' title='Delete' text={minimal ? null : 'Delete'} text={minimal ? null : t('emails.comment.delete.buttonTitle')} intent='danger' /> <Alert isOpen={alertOpen} canEscapeKeyCancel canOutsideClickCancel cancelButtonText='Cancel' cancelButtonText={t('emails.comment.delete.cancel')} onCancel={() => setAlertOpen(false)} confirmButtonText='Delete' confirmButtonText={t('emails.comment.delete.buttonTitle')} icon='trash' intent='danger' onConfirm={onClick} onClose={() => setAlertOpen(false)} loading={fetching} > <p>Are you sure you want to delete this comment?</p> <p>{t('emails.comment.delete.confirmation')}</p> </Alert> </> ) Loading frontend/src/actionlog/InjectMessage/InjectComment.tsx +16 −6 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import { type FormEvent, } from 'react' import { useTranslationFrontend } from '@inject/locale' import DeleteCommentButton from './DeleteCommentButton' const textArea = css` Loading Loading @@ -60,6 +61,7 @@ const InjectComment: FC<InjectCommentProps> = ({ comment: '', all: '', }) const { t } = useTranslationFrontend() const [{ fetching: addLoading }, addComment] = useTypedMutation(AddInstructorComment) Loading Loading @@ -182,7 +184,11 @@ const InjectComment: FC<InjectCommentProps> = ({ } }} icon={instructorComment ? 'comment' : 'add'} title={instructorComment ? 'Edit a comment' : 'Add a comment'} title={ instructorComment ? t('emails.comment.editComment') : t('emails.comment.addComment') } > <form onSubmit={ Loading @@ -193,13 +199,13 @@ const InjectComment: FC<InjectCommentProps> = ({ > <DialogBody> <FormGroup label='Message' label={t('emails.comment.message')} helperText={errors.comment ? errors.comment : null} intent={errors.comment ? 'danger' : 'none'} > <TextArea fill placeholder={'Your comment..'} placeholder={t('emails.comment.placeholder')} intent={errors.comment ? 'danger' : 'none'} value={comment} onChange={e => setComment(e.target.value)} Loading @@ -210,13 +216,13 @@ const InjectComment: FC<InjectCommentProps> = ({ <FormGroup labelFor='score' label='Score' label={t('emails.comment.score')} intent={errors.all ? 'danger' : 'none'} > <NumericInput intent={errors.all ? 'danger' : 'none'} id='score' title='Score' title={t('emails.comment.score')} fill value={score} onValueChange={value => { Loading Loading @@ -245,7 +251,11 @@ const InjectComment: FC<InjectCommentProps> = ({ loading={addLoading || editLoading} intent='primary' icon={instructorComment ? 'edit' : 'add'} text={instructorComment ? 'Edit' : 'Add'} text={ instructorComment ? t('emails.comment.edit') : t('emails.comment.add') } className={cx({ [wiggleClass]: wiggling })} /> </ButtonGroup> Loading frontend/src/routes/_protected/_navbar/users/index.tsx +8 −6 Original line number Diff line number Diff line Loading @@ -8,6 +8,7 @@ import { useClient, useTypedQuery, } from '@inject/graphql' import { useTranslationFrontend } from '@inject/locale' import type { Section } from '@inject/shared' import { Keys, Loading Loading @@ -77,6 +78,7 @@ const RouteComponent = () => { listenStorageChange: true, } ) const { t } = useTranslationFrontend() const [selectedUsers, setSelectedUsers] = useState<string[]>([]) const [{ data }] = useTypedQuery({ Loading @@ -103,7 +105,7 @@ const RouteComponent = () => { const sections: Section[] = [ { id: 'options', name: 'Options', name: t('userPanel.options.title'), node: ( <> <UserCreator /> Loading @@ -127,7 +129,7 @@ const RouteComponent = () => { client.mutation(ReloadTable, {}).toPromise() }} withLabel text='Reload list' text={t('userPanel.options.reloader.label')} /> <RemoveUsers userIds={selectedUsers} Loading @@ -144,7 +146,7 @@ const RouteComponent = () => { }, { id: 'filters', name: 'Filters', name: t('userPanel.filters.title'), node: ( <> <UserGroups selected={groups} setSelected={setGroups} /> Loading @@ -155,7 +157,7 @@ const RouteComponent = () => { }, { id: 'tags', name: 'Tags', name: t('userPanel.tags.title'), node: ( <> <AssignTags Loading @@ -168,12 +170,12 @@ const RouteComponent = () => { }, { id: 'domains', name: 'Restricted domains', name: t('userPanel.domains.title'), node: <RestrictedDomains />, }, { id: 'tokens', name: 'API Tokens', name: t('userPanel.tokens.title'), node: <ApiToken />, }, ] Loading frontend/src/users/GuestUsersCreator/CreateGuestUsersForm.tsx +83 −73 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ import { } from '@blueprintjs/core' import { DateInput3 } from '@blueprintjs/datetime2' import { css, cx } from '@emotion/css' import { useTranslationFrontend } from '@inject/locale' import { ErrorMessage, wiggleClass } from '@inject/shared' import type { Dispatch, FC, FormEvent, SetStateAction } from 'react' Loading Loading @@ -34,14 +35,20 @@ export const CreateGuestUsersForm: FC<CreateGuestUsersDialogProps> = ({ errors, loading, wiggling, }) => ( }) => { const { t } = useTranslationFrontend() return ( <form onSubmit={onSubmit}> <DialogBody> <FormGroup intent={errors.count ? 'danger' : 'none'} label={'Number of guest users'} label={t('userPanel.options.createGuestUser.dialog.numberOfUsers')} helperText={ errors.count ? 'Select a number of guest users to create' : undefined errors.count ? t( 'userPanel.options.createGuestUser.dialog.numberOfUsersHelper' ) : undefined } > <NumericInput Loading @@ -67,10 +74,12 @@ export const CreateGuestUsersForm: FC<CreateGuestUsersDialogProps> = ({ margin: 0 !important; padding-top: 0.3rem; `} label='Expiration date and time' label={t('userPanel.options.createGuestUser.dialog.expirationDate')} helperText={ errors.expiration ? 'Select an expiration date and time for the guest users' ? t( 'userPanel.options.createGuestUser.dialog.expirationDateHelper' ) : undefined } intent={errors.expiration ? 'danger' : 'none'} Loading Loading @@ -110,3 +119,4 @@ export const CreateGuestUsersForm: FC<CreateGuestUsersDialogProps> = ({ /> </form> ) } frontend/src/users/GuestUsersCreator/index.tsx +4 −2 Original line number Diff line number Diff line import { Button, Dialog } from '@blueprintjs/core' import type { CustomOperationContext, IUser } from '@inject/graphql' import { CreateGuestUsers, useTypedMutation } from '@inject/graphql' import { useTranslationFrontend } from '@inject/locale' import { dialog, Keys, useWiggle } from '@inject/shared' import { useLocalStorageState } from 'ahooks' import type { FormEvent } from 'react' Loading @@ -15,6 +16,7 @@ export const GuestUsersCreator = () => { const [count, setCount] = useState<number>() const [expiration, setExpiration] = useState<string | null>(null) const { t } = useTranslationFrontend() const [errors, setErrors] = useState<{ count: boolean Loading Loading @@ -105,7 +107,7 @@ export const GuestUsersCreator = () => { alignText='left' icon='array-timestamp' > Create guest users {t('userPanel.options.createGuestUser.label')} </Button> <Dialog Loading @@ -113,7 +115,7 @@ export const GuestUsersCreator = () => { canEscapeKeyClose={!createdUsers} isCloseButtonShown={!createdUsers} className={dialog} title='Create guest users' title={t('userPanel.options.createGuestUser.label')} isOpen={open} onClose={() => { setOpen(false) Loading Loading
frontend/src/actionlog/InjectMessage/DeleteCommentButton.tsx +6 −4 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ import { DeleteInstructorComment, useTypedMutation, } from '@inject/graphql' import { useTranslationFrontend } from '@inject/locale' import { type FC, useState } from 'react' type DeleteCommentButtonProps = { Loading @@ -30,6 +31,7 @@ const DeleteCommentButton: FC<DeleteCommentButtonProps> = ({ const [{ fetching }, deleteComment] = useTypedMutation( DeleteInstructorComment ) const { t } = useTranslationFrontend() const onClick = () => { deleteComment({ commentId, exerciseId }, { Loading Loading @@ -63,23 +65,23 @@ const DeleteCommentButton: FC<DeleteCommentButtonProps> = ({ type='button' icon='trash' title='Delete' text={minimal ? null : 'Delete'} text={minimal ? null : t('emails.comment.delete.buttonTitle')} intent='danger' /> <Alert isOpen={alertOpen} canEscapeKeyCancel canOutsideClickCancel cancelButtonText='Cancel' cancelButtonText={t('emails.comment.delete.cancel')} onCancel={() => setAlertOpen(false)} confirmButtonText='Delete' confirmButtonText={t('emails.comment.delete.buttonTitle')} icon='trash' intent='danger' onConfirm={onClick} onClose={() => setAlertOpen(false)} loading={fetching} > <p>Are you sure you want to delete this comment?</p> <p>{t('emails.comment.delete.confirmation')}</p> </Alert> </> ) Loading
frontend/src/actionlog/InjectMessage/InjectComment.tsx +16 −6 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import { type FormEvent, } from 'react' import { useTranslationFrontend } from '@inject/locale' import DeleteCommentButton from './DeleteCommentButton' const textArea = css` Loading Loading @@ -60,6 +61,7 @@ const InjectComment: FC<InjectCommentProps> = ({ comment: '', all: '', }) const { t } = useTranslationFrontend() const [{ fetching: addLoading }, addComment] = useTypedMutation(AddInstructorComment) Loading Loading @@ -182,7 +184,11 @@ const InjectComment: FC<InjectCommentProps> = ({ } }} icon={instructorComment ? 'comment' : 'add'} title={instructorComment ? 'Edit a comment' : 'Add a comment'} title={ instructorComment ? t('emails.comment.editComment') : t('emails.comment.addComment') } > <form onSubmit={ Loading @@ -193,13 +199,13 @@ const InjectComment: FC<InjectCommentProps> = ({ > <DialogBody> <FormGroup label='Message' label={t('emails.comment.message')} helperText={errors.comment ? errors.comment : null} intent={errors.comment ? 'danger' : 'none'} > <TextArea fill placeholder={'Your comment..'} placeholder={t('emails.comment.placeholder')} intent={errors.comment ? 'danger' : 'none'} value={comment} onChange={e => setComment(e.target.value)} Loading @@ -210,13 +216,13 @@ const InjectComment: FC<InjectCommentProps> = ({ <FormGroup labelFor='score' label='Score' label={t('emails.comment.score')} intent={errors.all ? 'danger' : 'none'} > <NumericInput intent={errors.all ? 'danger' : 'none'} id='score' title='Score' title={t('emails.comment.score')} fill value={score} onValueChange={value => { Loading Loading @@ -245,7 +251,11 @@ const InjectComment: FC<InjectCommentProps> = ({ loading={addLoading || editLoading} intent='primary' icon={instructorComment ? 'edit' : 'add'} text={instructorComment ? 'Edit' : 'Add'} text={ instructorComment ? t('emails.comment.edit') : t('emails.comment.add') } className={cx({ [wiggleClass]: wiggling })} /> </ButtonGroup> Loading
frontend/src/routes/_protected/_navbar/users/index.tsx +8 −6 Original line number Diff line number Diff line Loading @@ -8,6 +8,7 @@ import { useClient, useTypedQuery, } from '@inject/graphql' import { useTranslationFrontend } from '@inject/locale' import type { Section } from '@inject/shared' import { Keys, Loading Loading @@ -77,6 +78,7 @@ const RouteComponent = () => { listenStorageChange: true, } ) const { t } = useTranslationFrontend() const [selectedUsers, setSelectedUsers] = useState<string[]>([]) const [{ data }] = useTypedQuery({ Loading @@ -103,7 +105,7 @@ const RouteComponent = () => { const sections: Section[] = [ { id: 'options', name: 'Options', name: t('userPanel.options.title'), node: ( <> <UserCreator /> Loading @@ -127,7 +129,7 @@ const RouteComponent = () => { client.mutation(ReloadTable, {}).toPromise() }} withLabel text='Reload list' text={t('userPanel.options.reloader.label')} /> <RemoveUsers userIds={selectedUsers} Loading @@ -144,7 +146,7 @@ const RouteComponent = () => { }, { id: 'filters', name: 'Filters', name: t('userPanel.filters.title'), node: ( <> <UserGroups selected={groups} setSelected={setGroups} /> Loading @@ -155,7 +157,7 @@ const RouteComponent = () => { }, { id: 'tags', name: 'Tags', name: t('userPanel.tags.title'), node: ( <> <AssignTags Loading @@ -168,12 +170,12 @@ const RouteComponent = () => { }, { id: 'domains', name: 'Restricted domains', name: t('userPanel.domains.title'), node: <RestrictedDomains />, }, { id: 'tokens', name: 'API Tokens', name: t('userPanel.tokens.title'), node: <ApiToken />, }, ] Loading
frontend/src/users/GuestUsersCreator/CreateGuestUsersForm.tsx +83 −73 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ import { } from '@blueprintjs/core' import { DateInput3 } from '@blueprintjs/datetime2' import { css, cx } from '@emotion/css' import { useTranslationFrontend } from '@inject/locale' import { ErrorMessage, wiggleClass } from '@inject/shared' import type { Dispatch, FC, FormEvent, SetStateAction } from 'react' Loading Loading @@ -34,14 +35,20 @@ export const CreateGuestUsersForm: FC<CreateGuestUsersDialogProps> = ({ errors, loading, wiggling, }) => ( }) => { const { t } = useTranslationFrontend() return ( <form onSubmit={onSubmit}> <DialogBody> <FormGroup intent={errors.count ? 'danger' : 'none'} label={'Number of guest users'} label={t('userPanel.options.createGuestUser.dialog.numberOfUsers')} helperText={ errors.count ? 'Select a number of guest users to create' : undefined errors.count ? t( 'userPanel.options.createGuestUser.dialog.numberOfUsersHelper' ) : undefined } > <NumericInput Loading @@ -67,10 +74,12 @@ export const CreateGuestUsersForm: FC<CreateGuestUsersDialogProps> = ({ margin: 0 !important; padding-top: 0.3rem; `} label='Expiration date and time' label={t('userPanel.options.createGuestUser.dialog.expirationDate')} helperText={ errors.expiration ? 'Select an expiration date and time for the guest users' ? t( 'userPanel.options.createGuestUser.dialog.expirationDateHelper' ) : undefined } intent={errors.expiration ? 'danger' : 'none'} Loading Loading @@ -110,3 +119,4 @@ export const CreateGuestUsersForm: FC<CreateGuestUsersDialogProps> = ({ /> </form> ) }
frontend/src/users/GuestUsersCreator/index.tsx +4 −2 Original line number Diff line number Diff line import { Button, Dialog } from '@blueprintjs/core' import type { CustomOperationContext, IUser } from '@inject/graphql' import { CreateGuestUsers, useTypedMutation } from '@inject/graphql' import { useTranslationFrontend } from '@inject/locale' import { dialog, Keys, useWiggle } from '@inject/shared' import { useLocalStorageState } from 'ahooks' import type { FormEvent } from 'react' Loading @@ -15,6 +16,7 @@ export const GuestUsersCreator = () => { const [count, setCount] = useState<number>() const [expiration, setExpiration] = useState<string | null>(null) const { t } = useTranslationFrontend() const [errors, setErrors] = useState<{ count: boolean Loading Loading @@ -105,7 +107,7 @@ export const GuestUsersCreator = () => { alignText='left' icon='array-timestamp' > Create guest users {t('userPanel.options.createGuestUser.label')} </Button> <Dialog Loading @@ -113,7 +115,7 @@ export const GuestUsersCreator = () => { canEscapeKeyClose={!createdUsers} isCloseButtonShown={!createdUsers} className={dialog} title='Create guest users' title={t('userPanel.options.createGuestUser.label')} isOpen={open} onClose={() => { setOpen(false) Loading