diff --git a/packages/client/public/locales/en/translation.json b/packages/client/public/locales/en/translation.json index 404144b9..a1606a50 100644 --- a/packages/client/public/locales/en/translation.json +++ b/packages/client/public/locales/en/translation.json @@ -127,5 +127,23 @@ "tagView": { "originalEntry": "Original Entry" } + }, + "errors": { + "datasetCreate": "Failed to create dataset, please try again or report this issue", + "uploadSessionCreate": "Could not start the upload process, please try again", + "csvUpload": "Could not upload the CSV, please try again", + "entryUpload": "Could not upload entry, please try again", + "entryUploadComplete": "Could not complete the upload process, please try again", + "entryQuery": "Failed to get entries for the dataset", + "studyExists": "Could not check if a study with that name exists", + "datasetsForProject": "Failed to get possible datasets", + "tagComplete": "Failed to submit tag, please try again", + "datasetPermissionUpdate": "Could not give project permission", + "projectCreate": "Could not create the project, please try again", + "projectDelete": "An issue took place deleting the project", + "projectAdminUpdate": "Could not update the user's permssions", + "studyDelete": "Failed to delete the target study", + "studyCreate": "Failed to create the new study", + "tagsQuery": "Failed to get tags" } } diff --git a/packages/client/src/App.tsx b/packages/client/src/App.tsx index 74234797..cf4417bb 100644 --- a/packages/client/src/App.tsx +++ b/packages/client/src/App.tsx @@ -29,6 +29,7 @@ import { DatasetProvider } from './context/Dataset.context'; import { EntryControls } from './pages/studies/EntryControls'; import { PermissionProvider } from './context/Permission.context'; import { TagTrainingView } from './pages/studies/TagTrainingView'; +import { SnackbarProvider } from './context/Snackbar.context'; const drawerWidth = 256; const Main = styled('main', { shouldForwardProp: (prop) => prop !== 'open' })<{ @@ -74,8 +75,10 @@ const App: FC = () => { - - + + + + diff --git a/packages/client/src/components/AddDataset.component.tsx b/packages/client/src/components/AddDataset.component.tsx index de2bc443..8d7f8113 100644 --- a/packages/client/src/components/AddDataset.component.tsx +++ b/packages/client/src/components/AddDataset.component.tsx @@ -8,6 +8,8 @@ import { materialRenderers, materialCells } from '@jsonforms/material-renderers' import { useCreateDatasetMutation, useDatasetExistsLazyQuery } from '../graphql/dataset/dataset'; import { Button } from '@mui/material'; import { ErrorObject } from 'ajv'; +import { useSnackbar } from '../context/Snackbar.context'; +import { useTranslation } from 'react-i18next'; interface ShowProps { show: boolean; @@ -53,7 +55,11 @@ export const AddDataset: React.FC = (props: ShowProps) => { const initialData = {} as { name: string; description: string }; const [data, setData] = useState(initialData); - const [createDataset, { data: createDatasetResults, loading }] = useCreateDatasetMutation(); + const [createDataset, { data: createDatasetResults, loading, error: createDatasetError }] = + useCreateDatasetMutation(); + + const { t } = useTranslation(); + const { pushSnackbarMessage } = useSnackbar(); useEffect(() => { if (datasetExistsResults.data?.datasetExists) { @@ -74,9 +80,11 @@ export const AddDataset: React.FC = (props: ShowProps) => { useEffect(() => { if (createDatasetResults?.createDataset) { props.toggleModal(true); + } else if (createDatasetError) { + pushSnackbarMessage(t('errors.datasetCreate'), 'error'); + console.error(createDatasetError); } - //TODO handle creation server error with snackbar - }, [createDatasetResults]); + }, [createDatasetResults, createDatasetError]); const handleChange = (data: any, errors: ErrorObject[] | undefined) => { setData(data); diff --git a/packages/client/src/components/DatasetTable.component.tsx b/packages/client/src/components/DatasetTable.component.tsx index 4fc93808..c21dbb2d 100644 --- a/packages/client/src/components/DatasetTable.component.tsx +++ b/packages/client/src/components/DatasetTable.component.tsx @@ -4,6 +4,7 @@ import { Dataset, Entry } from '../graphql/graphql'; import { useEntryForDatasetLazyQuery } from '../graphql/entry/entry'; import { EntryView } from './EntryView.component'; import { useTranslation } from 'react-i18next'; +import { useSnackbar } from '../context/Snackbar.context'; export interface DatasetTableProps { dataset: Dataset; @@ -12,6 +13,7 @@ export interface DatasetTableProps { export const DatasetTable: React.FC = (props) => { const { t } = useTranslation(); + const { pushSnackbarMessage } = useSnackbar(); const defaultColumns: GridColDef[] = [ { @@ -41,6 +43,9 @@ export const DatasetTable: React.FC = (props) => { useEffect(() => { if (entryForDatasetResult.data) { setEntries(entryForDatasetResult.data.entryForDataset); + } else if (entryForDatasetResult.error) { + pushSnackbarMessage(t('errors.entryQuery'), 'error'); + console.error(entryForDatasetResult.error); } }, [entryForDatasetResult]); diff --git a/packages/client/src/components/NewStudyJsonForm.component.tsx b/packages/client/src/components/NewStudyJsonForm.component.tsx index 34634d87..1ae412a7 100644 --- a/packages/client/src/components/NewStudyJsonForm.component.tsx +++ b/packages/client/src/components/NewStudyJsonForm.component.tsx @@ -7,6 +7,7 @@ import { StudyExistsQuery, StudyExistsQueryVariables, StudyExistsDocument } from import { useProject } from '../context/Project.context'; import { useTranslation } from 'react-i18next'; import { useApolloClient } from '@apollo/client'; +import { useSnackbar } from '../context/Snackbar.context'; export interface NewStudyFormProps { newStudy: PartialStudyCreate | null; @@ -15,6 +16,7 @@ export interface NewStudyFormProps { export const NewStudyJsonForm: React.FC = (props) => { const { t } = useTranslation(); + const { pushSnackbarMessage } = useSnackbar(); const schema = { type: 'object', @@ -110,6 +112,10 @@ export const NewStudyJsonForm: React.FC = (props) => { ]); props.setNewStudy(null); return; + } else if (exists.error) { + pushSnackbarMessage(t('errors.studyExists'), 'error'); + props.setNewStudy(null); + return; } // No errors diff --git a/packages/client/src/components/TagTraining.component.tsx b/packages/client/src/components/TagTraining.component.tsx index 21500ab1..7afc0a0a 100644 --- a/packages/client/src/components/TagTraining.component.tsx +++ b/packages/client/src/components/TagTraining.component.tsx @@ -5,6 +5,8 @@ import { Dataset, Entry } from '../graphql/graphql'; import { GridColDef } from '@mui/x-data-grid'; import { Switch } from '@mui/material'; import { useProject } from '../context/Project.context'; +import { useTranslation } from 'react-i18next'; +import { useSnackbar } from '../context/Snackbar.context'; export interface TagTrainingComponentProps { setTrainingSet: Dispatch>; @@ -17,6 +19,8 @@ export const TagTrainingComponent: React.FC = (props) const [getDatasetsQuery, getDatasetsResults] = useGetDatasetsByProjectLazyQuery(); const [trainingSet, setTrainingSet] = useState([]); const [taggingSet, setTaggingSet] = useState([]); + const { t } = useTranslation(); + const { pushSnackbarMessage } = useSnackbar(); useEffect(() => { if (project) { @@ -76,8 +80,11 @@ export const TagTrainingComponent: React.FC = (props) useEffect(() => { if (getDatasetsResults.data) { setDatasets(getDatasetsResults.data.getDatasetsByProject); + } else if (getDatasetsResults.error) { + pushSnackbarMessage(t('errors.datasetsForProject'), 'error'); + console.error(getDatasetsResults.error); } - }, [getDatasetsResults.data]); + }, [getDatasetsResults]); return ( <> diff --git a/packages/client/src/components/upload/CSVUpload.component.tsx b/packages/client/src/components/upload/CSVUpload.component.tsx index a438540c..0a6c3e0c 100644 --- a/packages/client/src/components/upload/CSVUpload.component.tsx +++ b/packages/client/src/components/upload/CSVUpload.component.tsx @@ -10,6 +10,8 @@ import { useApolloClient } from '@apollo/client'; import axios from 'axios'; import { Box, Button } from '@mui/material'; import UploadIcon from '@mui/icons-material/Upload'; +import { useSnackbar } from '../../context/Snackbar.context'; +import { useTranslation } from 'react-i18next'; export interface CSVUploadProps { dataset: Dataset | null; @@ -26,6 +28,8 @@ export const CSVUpload: React.FC = ({ setCsvValid }) => { const apolloClient = useApolloClient(); + const { pushSnackbarMessage } = useSnackbar(); + const { t } = useTranslation(); // Implemented with using the apollo client directly instead of the useMutation hook // to reduce the need for multiple use effects to handle each step change @@ -42,7 +46,8 @@ export const CSVUpload: React.FC = ({ }); if (!sessionCreation.data?.createUploadSession) { - console.error('Failed to create upload session'); + pushSnackbarMessage(t('errors.uploadSessionCreate'), 'error'); + console.error(sessionCreation.errors); return; } @@ -56,7 +61,8 @@ export const CSVUpload: React.FC = ({ }); if (!uploadUrlQuery.data?.getCSVUploadURL) { - console.error('Failed to get upload url'); + pushSnackbarMessage(t('errors.csvUpload'), 'error'); + console.error(uploadUrlQuery.error); return; } @@ -70,7 +76,8 @@ export const CSVUpload: React.FC = ({ }); if (upload.status != 200) { - console.error('Failed to upload CSV'); + pushSnackbarMessage(t('errors.csvUpload'), 'error'); + console.error(uploadUrlQuery.error); return; } diff --git a/packages/client/src/components/upload/EntryUpload.component.tsx b/packages/client/src/components/upload/EntryUpload.component.tsx index ffa2e67f..44f23186 100644 --- a/packages/client/src/components/upload/EntryUpload.component.tsx +++ b/packages/client/src/components/upload/EntryUpload.component.tsx @@ -6,6 +6,8 @@ import { UploadSession, UploadStatus } from '../../graphql/graphql'; import axios from 'axios'; import { Dispatch, SetStateAction, useState } from 'react'; import { StatusMessage } from '../../models/StatusMessage'; +import { useTranslation } from 'react-i18next'; +import { useSnackbar } from '../../context/Snackbar.context'; export interface EntryUploadProps { uploadSession: UploadSession | null; @@ -24,6 +26,9 @@ export const EntryUpload: React.FC = ({ const [uploadProgress, setUploadProgress] = useState(0); const [uploadComplete, setUploadComplete] = useState(false); + const { t } = useTranslation(); + const { pushSnackbarMessage } = useSnackbar(); + const handleChange = async (event: React.ChangeEvent) => { if (!uploadSession) { console.error('No upload session'); @@ -47,7 +52,8 @@ export const EntryUpload: React.FC = ({ }); if (!uploadUrlQuery.data?.getEntryUploadURL) { - console.error('Failed to get upload url'); + pushSnackbarMessage(t('errors.entryUpload'), 'error'); + console.error(uploadUrlQuery.errors); return; } @@ -71,7 +77,8 @@ export const EntryUpload: React.FC = ({ const completionData = completionResult.data?.completeUploadSession; if (!completionData) { - console.error('Failed to complete upload session'); + pushSnackbarMessage(t('errors.entryUploadComplete'), 'error'); + console.error(completionData.errors); return; } diff --git a/packages/client/src/context/Snackbar.context.tsx b/packages/client/src/context/Snackbar.context.tsx new file mode 100644 index 00000000..9c7db744 --- /dev/null +++ b/packages/client/src/context/Snackbar.context.tsx @@ -0,0 +1,70 @@ +import { Alert, Snackbar, Stack } from '@mui/material'; +import { createContext, FC, useState, useContext } from 'react'; + +type SnackbarType = 'success' | 'error' | 'warning' | 'info'; + +export interface SnackbarContextProps { + pushSnackbarMessage: (message: string, type?: SnackbarType) => void; +} + +const SnackbarContext = createContext({} as SnackbarContextProps); + +export interface SnackbarProviderProps { + children: React.ReactNode; +} + +export interface SnackbarMessage { + id: string; + message: string; + type: SnackbarType; +} + +export const SnackbarProvider: FC = ({ children, ...props }) => { + const [messages, setMessages] = useState([]); + + const pushSnackbarMessage = (message: string, type: SnackbarType = 'error') => { + setMessages([ + ...messages, + { + message, + type, + id: Math.random().toString(36) + } + ]); + }; + + const handleClose = (id: string, reason?: string) => { + if (reason === 'clickaway') { + return; + } + setMessages(messages.filter((message) => message.id !== id)); + }; + + return ( + + {children} + + {messages.map((message) => ( + handleClose(message.id)} + > + handleClose(message.id)} + sx={{ width: '100%' }} + > + {message.message} + + + ))} + + + ); +}; + +export const useSnackbar = () => useContext(SnackbarContext); diff --git a/packages/client/src/pages/contribute/TaggingInterface.tsx b/packages/client/src/pages/contribute/TaggingInterface.tsx index edb6f3bd..5d27675f 100644 --- a/packages/client/src/pages/contribute/TaggingInterface.tsx +++ b/packages/client/src/pages/contribute/TaggingInterface.tsx @@ -7,6 +7,8 @@ import { useCompleteTagMutation } from '../../graphql/tag/tag'; import { Study } from '../../graphql/graphql'; import { TagProvider, useTag } from '../../context/Tag.context'; import { NoTagNotification } from '../../components/contribute/NoTagNotification.component'; +import { useTranslation } from 'react-i18next'; +import { useSnackbar } from '../../context/Snackbar.context'; export const TaggingInterface: React.FC = () => { const { study } = useStudy(); @@ -31,6 +33,9 @@ const MainView: React.FC = (props) => { const [completeTag, completeTagResult] = useCompleteTagMutation(); const [tagData, setTagData] = useState({}); + const { t } = useTranslation(); + const { pushSnackbarMessage } = useSnackbar(); + // Changes made to the tag data useEffect(() => { if (tagData && tag) { @@ -45,8 +50,11 @@ const MainView: React.FC = (props) => { if (completeTagResult.data) { // Assign a new tag requestTag(); + } else if (completeTagResult.error) { + pushSnackbarMessage(t('errors.tagComplete'), 'error'); + console.error(completeTagResult.error); } - }, [completeTagResult.data]); + }, [completeTagResult]); return ( <> diff --git a/packages/client/src/pages/datasets/ProjectAccess.tsx b/packages/client/src/pages/datasets/ProjectAccess.tsx index c0fc8a48..32f78fc2 100644 --- a/packages/client/src/pages/datasets/ProjectAccess.tsx +++ b/packages/client/src/pages/datasets/ProjectAccess.tsx @@ -6,6 +6,7 @@ import { useEffect, useState } from 'react'; import { DatasetProjectPermission, Project } from '../../graphql/graphql'; import { useGrantProjectDatasetAccessMutation } from '../../graphql/permission/permission'; import { useTranslation } from 'react-i18next'; +import { useSnackbar } from '../../context/Snackbar.context'; export const ProjectAccess: React.FC = () => { const { project } = useProject(); @@ -13,6 +14,7 @@ export const ProjectAccess: React.FC = () => { const [projectAccess, setProjectAccess] = useState([]); const [grantProjectDatasetAccess, grantProjectDatasetAccessResults] = useGrantProjectDatasetAccessMutation(); const { t } = useTranslation(); + const { pushSnackbarMessage } = useSnackbar(); // For querying for the permissions useEffect(() => { @@ -25,6 +27,9 @@ export const ProjectAccess: React.FC = () => { useEffect(() => { if (datasetProjectPermissionResults.data) { setProjectAccess(datasetProjectPermissionResults.data.getDatasetProjectPermissions); + } else if (datasetProjectPermissionResults.error) { + pushSnackbarMessage(t('errors.datasetPermissionUpdate'), 'error'); + console.error(datasetProjectPermissionResults.error); } }, [datasetProjectPermissionResults]); diff --git a/packages/client/src/pages/projects/NewProject.tsx b/packages/client/src/pages/projects/NewProject.tsx index abe14f94..7158e448 100644 --- a/packages/client/src/pages/projects/NewProject.tsx +++ b/packages/client/src/pages/projects/NewProject.tsx @@ -6,6 +6,7 @@ import { JsonForms } from '@jsonforms/react'; import { useCreateProjectMutation, useProjectExistsLazyQuery } from '../../graphql/project/project'; import { ErrorObject } from 'ajv'; import { useTranslation } from 'react-i18next'; +import { useSnackbar } from '../../context/Snackbar.context'; const initialData = { name: '', @@ -21,6 +22,7 @@ export const NewProject: React.FC = () => { const [projectExistsQuery, projectExistsResults] = useProjectExistsLazyQuery(); const [additionalErrors, setAdditionalErrors] = useState([]); const { t } = useTranslation(); + const { pushSnackbarMessage } = useSnackbar(); const schema = { type: 'object', @@ -84,7 +86,8 @@ export const NewProject: React.FC = () => { useEffect(() => { if (error) { - //handle server side error here. For now a simple text is displayed + pushSnackbarMessage(t('errors.projectCreate'), 'error'); + console.error(error); } }, [error]); diff --git a/packages/client/src/pages/projects/ProjectControl.tsx b/packages/client/src/pages/projects/ProjectControl.tsx index 894a84b2..2d0c3aa9 100644 --- a/packages/client/src/pages/projects/ProjectControl.tsx +++ b/packages/client/src/pages/projects/ProjectControl.tsx @@ -8,6 +8,7 @@ import { useDeleteProjectMutation } from '../../graphql/project/project'; import { useConfirmation } from '../../context/Confirmation.context'; import { useEffect } from 'react'; import { useTranslation } from 'react-i18next'; +import { useSnackbar } from '../../context/Snackbar.context'; const ProjectControl: React.FC = () => { const { projects, updateProjectList } = useProject(); @@ -15,6 +16,7 @@ const ProjectControl: React.FC = () => { const [deleteProjectMutation, deleteProjectResults] = useDeleteProjectMutation(); const confirmation = useConfirmation(); const { t } = useTranslation(); + const { pushSnackbarMessage } = useSnackbar(); const handleDelete = async (id: GridRowId) => { // Execute delete mutation @@ -32,8 +34,11 @@ const ProjectControl: React.FC = () => { useEffect(() => { if (deleteProjectResults.called && deleteProjectResults.data) { updateProjectList(); + } else if (deleteProjectResults.error) { + pushSnackbarMessage(t('errors.projectDelete'), 'error'); + console.error(deleteProjectResults.error); } - }, [deleteProjectResults.data, deleteProjectResults.called]); + }, [deleteProjectResults.data, deleteProjectResults.called, deleteProjectResults.error]); const columns: GridColDef[] = [ { diff --git a/packages/client/src/pages/projects/ProjectUserPermissions.tsx b/packages/client/src/pages/projects/ProjectUserPermissions.tsx index 1b2307a1..dd50985b 100644 --- a/packages/client/src/pages/projects/ProjectUserPermissions.tsx +++ b/packages/client/src/pages/projects/ProjectUserPermissions.tsx @@ -7,6 +7,7 @@ import { useGetProjectPermissionsQuery } from '../../graphql/permission/permissi import { DecodedToken, useAuth } from '../../context/Auth.context'; import { useGrantProjectPermissionsMutation } from '../../graphql/permission/permission'; import { useTranslation } from 'react-i18next'; +import { useSnackbar } from '../../context/Snackbar.context'; export const ProjectUserPermissions: React.FC = () => { const { project } = useProject(); @@ -29,6 +30,8 @@ interface EditAdminSwitchProps { const EditAdminSwitch: React.FC = (props) => { const [grantProjectPermissions, grantProjectPermissionsResults] = useGrantProjectPermissionsMutation(); + const { t } = useTranslation(); + const { pushSnackbarMessage } = useSnackbar(); const handleChange = (event: ChangeEvent) => { grantProjectPermissions({ @@ -43,6 +46,9 @@ const EditAdminSwitch: React.FC = (props) => { useEffect(() => { if (grantProjectPermissionsResults.data) { props.refetch(); + } else if (grantProjectPermissionsResults.error) { + pushSnackbarMessage(t('errors.projectAdminUpdate'), 'error'); + console.error(grantProjectPermissionsResults.error); } }, [grantProjectPermissionsResults]); @@ -56,7 +62,7 @@ const EditAdminSwitch: React.FC = (props) => { }; const UserPermissionTable: React.FC<{ project: Project }> = ({ project }) => { - const { data, refetch } = useGetProjectPermissionsQuery({ + const { data, refetch, error } = useGetProjectPermissionsQuery({ variables: { project: project._id } @@ -65,12 +71,16 @@ const UserPermissionTable: React.FC<{ project: Project }> = ({ project }) => { const [rows, setRows] = useState([]); const { decodedToken } = useAuth(); const { t } = useTranslation(); + const { pushSnackbarMessage } = useSnackbar(); useEffect(() => { if (data?.getProjectPermissions) { setRows(data.getProjectPermissions); + } else if (error) { + pushSnackbarMessage(t('errors.projectAdminUpdate'), 'error'); + console.error(error); } - }, [data]); + }, [data, error]); const columns: GridColDef[] = [ /* For now, only email is populated, this will change in the future diff --git a/packages/client/src/pages/studies/EntryControls.tsx b/packages/client/src/pages/studies/EntryControls.tsx index 428238c7..86e12338 100644 --- a/packages/client/src/pages/studies/EntryControls.tsx +++ b/packages/client/src/pages/studies/EntryControls.tsx @@ -7,6 +7,7 @@ import { useGetDatasetsByProjectQuery } from '../../graphql/dataset/dataset'; import { useProject } from '../../context/Project.context'; import ToggleEntryEnabled from '../../components/ToggleEntryEnabled.component'; import { useTranslation } from 'react-i18next'; +import { useSnackbar } from '../../context/Snackbar.context'; export const EntryControls: React.FC = () => { const { project } = useProject(); @@ -17,12 +18,16 @@ export const EntryControls: React.FC = () => { } }); const { t } = useTranslation(); + const { pushSnackbarMessage } = useSnackbar(); useEffect(() => { if (getDatasetsByProjectResults.data) { setDatasets(getDatasetsByProjectResults.data.getDatasetsByProject); + } else if (getDatasetsByProjectResults.error) { + pushSnackbarMessage(t('errors.datasetsForProject'), 'error'); + console.error(getDatasetsByProjectResults.error); } - }, [getDatasetsByProjectResults.data]); + }, [getDatasetsByProjectResults]); const additionalColumns: GridColDef[] = [ { diff --git a/packages/client/src/pages/studies/NewStudy.tsx b/packages/client/src/pages/studies/NewStudy.tsx index 24023038..febdc27f 100644 --- a/packages/client/src/pages/studies/NewStudy.tsx +++ b/packages/client/src/pages/studies/NewStudy.tsx @@ -19,6 +19,7 @@ import { } from '../../graphql/tag/tag'; import { useTranslation } from 'react-i18next'; import { TagFieldFragmentSchema, TagField } from '../../components/tagbuilder/TagProvider'; +import { useSnackbar } from '../../context/Snackbar.context'; export const NewStudy: React.FC = () => { const [activeStep, setActiveStep] = useState(0); @@ -37,6 +38,7 @@ export const NewStudy: React.FC = () => { const [tagSchemaFragments, setTagSchemaFragments] = useState<(TagFieldFragmentSchema | null)[]>([]); const { t } = useTranslation(); + const { pushSnackbarMessage } = useSnackbar(); // Handles mantaining which step the user is on and the step limit useEffect(() => { @@ -83,7 +85,8 @@ export const NewStudy: React.FC = () => { }); if (result.errors || !result.data) { - console.error('Failed to create study'); + pushSnackbarMessage(t('errors.studyCreate'), 'error'); + console.error(result.errors); return; } diff --git a/packages/client/src/pages/studies/StudyControl.tsx b/packages/client/src/pages/studies/StudyControl.tsx index bda2db98..8daa93f3 100644 --- a/packages/client/src/pages/studies/StudyControl.tsx +++ b/packages/client/src/pages/studies/StudyControl.tsx @@ -8,6 +8,7 @@ import { useDeleteStudyMutation } from '../../graphql/study/study'; import { useEffect } from 'react'; import { useConfirmation } from '../../context/Confirmation.context'; import { useTranslation } from 'react-i18next'; +import { useSnackbar } from '../../context/Snackbar.context'; export const StudyControl: React.FC = () => { const { studies, updateStudies } = useStudy(); @@ -15,6 +16,7 @@ export const StudyControl: React.FC = () => { const [deleteStudyMutation, deleteStudyResults] = useDeleteStudyMutation(); const confirmation = useConfirmation(); const { t } = useTranslation(); + const { pushSnackbarMessage } = useSnackbar(); const handleDelete = async (id: GridRowId) => { // Execute delete mutation @@ -32,8 +34,11 @@ export const StudyControl: React.FC = () => { useEffect(() => { if (deleteStudyResults.called && deleteStudyResults.data) { updateStudies(); + } else if (deleteStudyResults.error) { + pushSnackbarMessage(t('errors.studyDelete'), 'error'); + console.error(deleteStudyResults.error); } - }, [deleteStudyResults.called, deleteStudyResults.data]); + }, [deleteStudyResults.called, deleteStudyResults.data, deleteStudyResults.error]); const columns: GridColDef[] = [ { diff --git a/packages/client/src/pages/studies/TagTrainingView.tsx b/packages/client/src/pages/studies/TagTrainingView.tsx index 3e0d920e..bf806617 100644 --- a/packages/client/src/pages/studies/TagTrainingView.tsx +++ b/packages/client/src/pages/studies/TagTrainingView.tsx @@ -5,6 +5,7 @@ import { useEffect, useState } from 'react'; import { TagGridView } from '../../components/tag/view/TagGridView.component'; import { Typography } from '@mui/material'; import { useTranslation } from 'react-i18next'; +import { useSnackbar } from '../../context/Snackbar.context'; export const TagTrainingView: React.FC = () => { const state = useLocation().state; @@ -12,12 +13,16 @@ export const TagTrainingView: React.FC = () => { const study: Study = state.study; const [tags, setTags] = useState([]); const { t } = useTranslation(); + const { pushSnackbarMessage } = useSnackbar(); const trainingTags = useGetTrainingTagsQuery({ variables: { study: study._id, user: user.uid } }); useEffect(() => { if (trainingTags.data) { setTags(trainingTags.data.getTrainingTags); + } else if (trainingTags.error) { + pushSnackbarMessage(t('errors.tagsQuery'), 'error'); + console.error(trainingTags.error); } }, [trainingTags]);