Skip to content

Commit

Permalink
fix: [PROD-13564] update generic table errors management
Browse files Browse the repository at this point in the history
Co-authored-by: Tristan Huet <tristan.huet@cosmotech.com>
Co-authored-by: Nicolas Borde <nicolas.borde@cosmotech.com>
  • Loading branch information
nborde-CSM and csm-thu committed Sep 25, 2024
1 parent b269f82 commit 0a1f318
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 92 deletions.
11 changes: 8 additions & 3 deletions public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -747,24 +747,29 @@
"labels": {
"loading": "Loading...",
"fileImportError": "File load failed.",
"queryFailedError": "Query failed",
"maxErrorsCount": "(only the top first {{maxCount}} results)",
"errorsCount_one": "{{count}} error occurred:",
"errorsCount_other": "{{count}} errors occurred:",
"placeholderTitle": "Import your first data",
"placeholderBody": "After importing a valid csv or xlsx file, your data will be displayed in an interactive table.",
"addRowPlaceholderTitle": "Import or create your first data",
"addRowPlaceholderBody": "Import a valid csv or xlsx file, or add a new row, so that your data will be displayed in an interactive table.",
"configErrorPlaceholderTitle": "Configuration error",
"noDataPlaceholderTitle": "No data",
"noDataPlaceholderBody": "No data to display.",
"import": "Import",
"export": "Export",
"addRow": "Add a new row",
"deleteRows": "Remove selected rows",
"fullscreen": "Fullscreen",
"revert": "Revert",
"noDatasetsError": "Impossible to fetch data from dataset because the list of datasets is empty",
"notTwingraphDatasetError": "Only twingraph datasets can be used to fetch table data dynamically",
"noQueryError": "Impossible to fetch data from dataset because there is no twingraph query defined for this parameter. You can load data manually using Import button",
"noQueryError": "Impossible to fetch data from dataset because there is no twingraph query defined for this parameter. You can load data manually using the Import button",
"NoQueryErrorImportFalse": "Impossible to fetch data from dataset because there is no twingraph query defined for this parameter.",
"wrongResultKeyError": "Returned result doesn't have {{resultKey}} property. Probably there is an error in resultKey configuration, please, check your solution",
"wrongQueryError": "Returned result is empty, there is probably an error in your query configuration. Please, check your solution"
"wrongQueryError": "Returned result is empty, there is probably an error in your query configuration. Please, check your solution",
"unknownQueryError": "Something went wrong when querying the table data. Please contact your administrator if the problem persists."
},
"export": {
"labels": {
Expand Down
9 changes: 7 additions & 2 deletions public/locales/fr/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -747,24 +747,29 @@
"labels": {
"loading": "Chargement en cours...",
"fileImportError": "Échec du chargement du fichier.",
"queryFailedError": "La requête a échoué",
"maxErrorsCount": "(seulement les {{maxCount}} premiers résultats)",
"errorsCount_one": "{{count}} erreur:",
"errorsCount_other": "{{count}} erreurs:",
"placeholderTitle": "Importez votre premier fichier",
"placeholderBody": "Une fois votre fichier csv ou xlsx importé, vos données seront affichées dans un tableau interactif.",
"addRowPlaceholderTitle": "Importez ou créez\nvos premières données",
"addRowPlaceholderBody": "Importez un fichier csv ou xlsx valide, ou ajoutez une nouvelle ligne, et ainsi afficher vos données sur un tableau interactif.",
"configErrorPlaceholderTitle": "Erreur de configuration",
"noDataPlaceholderTitle": "Aucune donnée",
"noDataPlaceholderBody": "Pas de données à afficher.",
"import": "Importer",
"export": "Exporter",
"addRow": "Ajouter une nouvelle ligne",
"deleteRows": "Supprimer les lignes sélectionnées",
"fullscreen": "Plein écran",
"revert": "Rétablir",
"noDatasetsError": "Il est impossible de charger les données depuis le dataset parce que la liste des datasets est vide",
"notTwingraphDatasetError": "Le chargement dynamique des données du tableau est disponible uniquement pour les datasets du type twingraph",
"noQueryError": "Il est impossible de charger les données depuis le dataset parce qu'aucune requête du twingraph n'est définie pour ce paramètre. Vous pouvez charger les données manuellement avec le bouton Importer",
"NoQueryErrorImportFalse": "Il est impossible de charger les données depuis le dataset parce qu'aucune requête du twingraph n'est définie pour ce paramètre.",
"wrongResultKeyError": "Le résultat de la requête ne contient pas de propriété {{resultKey}}. Cela provient probablement d'une erreur dans la configuration de resultKey, veuillez vérifier la solution.",
"wrongQueryError": "Le résultat retourné est vide. Cela provient probablement d'une erreur dans la configuration de la requête. Veuillez vérifier la solution."
"wrongQueryError": "Le résultat retourné est vide. Cela provient probablement d'une erreur dans la configuration de la requête. Veuillez vérifier la solution.",
"unknownQueryError": "Une erreur est survenue durant la requête sur les données du tableau. Si le problème persiste, merci de contacter votre administrateur."
},
"export": {
"labels": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import rfdc from 'rfdc';
import { AgGridUtils, FileBlobUtils } from '@cosmotech/core';
import { Table, TABLE_DATA_STATUS, UPLOAD_FILE_STATUS_KEY } from '@cosmotech/ui';
import { Api } from '../../../../services/config/Api';
import { useSetApplicationErrorMessage } from '../../../../state/hooks/ApplicationHooks';
import { useFindDatasetById, useDatasets } from '../../../../state/hooks/DatasetHooks.js';
import { useOrganizationId } from '../../../../state/hooks/OrganizationHooks.js';
import { useWorkspaceId } from '../../../../state/hooks/WorkspaceHooks.js';
import { gridLight, gridDark } from '../../../../theme/';
Expand Down Expand Up @@ -52,9 +52,9 @@ export const GenericTable = ({
const { t } = useTranslation();
const organizationId = useOrganizationId();
const workspaceId = useWorkspaceId();
const datasets = useSelector((state) => state.dataset?.list?.data);
const datasets = useDatasets();
const findDatasetById = useFindDatasetById();
const scenarioId = useSelector((state) => state.scenario?.current?.data?.id);
const setApplicationErrorMessage = useSetApplicationErrorMessage();
const canChangeRowsNumber = ConfigUtils.getParameterAttribute(parameterData, 'canChangeRowsNumber') ?? false;

const parameterId = parameterData.id;
Expand All @@ -65,24 +65,41 @@ export const GenericTable = ({
const [isConfirmRowsDeletionDialogOpen, setConfirmRowsDeletionDialogOpen] = useState(false);
const selectedRowsRef = useRef();

const tableLabels = {
label: t(TranslationUtils.getParameterTranslationKey(parameterId), parameterId),
loading: t('genericcomponent.table.labels.loading', 'Loading...'),
clearErrors: t('genericcomponent.table.button.clearErrors', 'Clear'),
errorsPanelMainError: t('genericcomponent.table.labels.fileImportError', 'File load failed.'),
placeholderTitle: canChangeRowsNumber
? t('genericcomponent.table.labels.addRowPlaceholderTitle', 'Import or create your first data')
: t('genericcomponent.table.labels.placeholderTitle', 'Import your first data'),
placeholderBody: canChangeRowsNumber
? t(
const [placeholder, setPlaceholder] = useState();

useEffect(() => {
if (context.visibilityOptions?.import === false) {
setPlaceholder({
title: t('genericcomponent.table.labels.noDataPlaceholderTitle', 'No data'),
body: t('genericcomponent.table.labels.noDataPlaceholderBody', 'No data to display.'),
});
} else if (canChangeRowsNumber) {
setPlaceholder({
title: t('genericcomponent.table.labels.addRowPlaceholderTitle', 'Import or create your first data'),
body: t(
'genericcomponent.table.labels.addRowPlaceholderBody',
'Import a valid csv or xlsx file,' +
'or add a new row, so that your data will be displayed in an interactive table.'
)
: t(
),
});
} else {
setPlaceholder({
title: t('genericcomponent.table.labels.placeholderTitle', 'Import your first data'),
body: t(
'genericcomponent.table.labels.placeholderBody',
'After importing a valid csv or xlsx file, your data will be displayed in an interactive table.'
),
});
}
}, [canChangeRowsNumber, context.visibilityOptions?.import, t]);

const tableLabels = {
label: t(TranslationUtils.getParameterTranslationKey(parameterId), parameterId),
loading: t('genericcomponent.table.labels.loading', 'Loading...'),
clearErrors: t('genericcomponent.table.button.clearErrors', 'Clear'),
errorsPanelMainError: t('genericcomponent.table.labels.fileImportError', 'File load failed.'),
placeholderTitle: placeholder?.title,
placeholderBody: placeholder?.body,
import: t('genericcomponent.table.labels.import', 'Import'),
export: t('genericcomponent.table.labels.export', 'Export'),
addRow: t('genericcomponent.table.labels.addRow', 'Add a new row'),
Expand Down Expand Up @@ -206,77 +223,112 @@ export const GenericTable = ({
return;
}
GenericTable.downloadLocked[lockId] = true;
const sourceDatasetId = context.targetDatasetId;

if (!sourceDatasetId) {
setPlaceholder({
title: t('genericcomponent.table.labels.noDataPlaceholderTitle', 'No data'),
body: t(
'genericcomponent.table.labels.noDatasetsError',
'Impossible to fetch data from dataset because the list of datasets is empty'
),
});
return;
}

const dynamicValuesConfig = ConfigUtils.getParameterAttribute(parameterData, 'dynamicValues');
const query = dynamicValuesConfig?.query;
if (!query) {
setPlaceholder({
title: t('genericcomponent.table.labels.noQueryPlaceholderTitle', 'No data'),
body: t(
'genericcomponent.table.labels.NoQueryErrorImportFalse',
'Impossible to fetch data from dataset because there is no twingraph query defined for this parameter. '
),
});
return;
}

if (findDatasetById(sourceDatasetId)?.ingestionStatus == null) {
setPlaceholder({
title: t('genericcomponent.table.labels.noTwingraph', 'No Twingraph'),
body: t(
'genericcomponent.table.labels.noTwingraphDatasetError',
'Only twingraph datasets can be used to fetch table data dynamically'
),
});
return;
}

const resultKey = dynamicValuesConfig?.resultKey;
let data;
try {
const sourceDatasetId = context.targetDatasetId;
if (!sourceDatasetId)
throw new Error(
t(
'genericcomponent.table.labels.noDatasetsError',
'Impossible to fetch data from dataset because the list of datasets is empty'
)
);
const dynamicValuesConfig = ConfigUtils.getParameterAttribute(parameterData, 'dynamicValues');
const query = dynamicValuesConfig?.query;
if (!query)
throw new Error(
t(
'genericcomponent.table.labels.noQueryError',
'Impossible to fetch data from dataset because there is no twingraph query defined for this parameter. ' +
'You can load data manually using Import button'
)
);
const resultKey = dynamicValuesConfig?.resultKey;
const { data } = await Api.Datasets.twingraphQuery(organizationId, sourceDatasetId, { query });
if (data.length === 0)
throw new Error(
t(
'genericcomponent.table.labels.wrongQueryError',
'Returned result is empty, there is probably an error in your query configuration. ' +
'Please, check your solution'
)
);
const rowsDict = data.map((row) => row[resultKey]);
if (rowsDict.includes(undefined))
throw new Error(
t(
'genericcomponent.table.labels.wrongResultKeyError',
`Returned result doesn't have ${resultKey} property.
Probably there is an error in resultKey configuration, please, check your solution`,
{ resultKey }
)
);
const csvRows = AgGridUtils.toCSV(rowsDict, columns);
const agGridData = _generateGridDataFromCSV(csvRows, parameterData);
if (agGridData.error) {
setClientFileDescriptor({
tableDataStatus: TABLE_DATA_STATUS.ERROR,
errors: agGridData.error,
});
} else
setClientFileDescriptor({
name: fileName,
file: null,
agGridRows: agGridData.rows,
errors: null,
status: UPLOAD_FILE_STATUS_KEY.READY_TO_UPLOAD,
tableDataStatus: TABLE_DATA_STATUS.READY,
uploadPreprocess: { content: _uploadPreprocess },
});
({ data } = await Api.Datasets.twingraphQuery(organizationId, sourceDatasetId, { query }));
} catch (error) {
const errorComment = error?.response?.status
? t(
'genericcomponent.table.labels.notTwingraphDatasetError',
'Only twingraph datasets can be used to fetch table data dynamically'
)
: '';
setApplicationErrorMessage(error, errorComment);
setPlaceholder({
title: error?.response?.statusText ?? t('genericcomponent.table.labels.queryFailedError', 'Query failed'),
body: t(
'genericcomponent.table.labels.unknownQueryError',
'Something went wrong when querying the table data. Please contact your administrator if the ' +
'problem persists.'
),
});
console.error(error?.response?.data?.detail);

setClientFileDescriptor({
file: null,
content: null,
agGridRows: null,
errors: null,
tableDataStatus: TABLE_DATA_STATUS.ERROR,
});
return;
}

if (data.length === 0) {
setPlaceholder({
title: t('genericcomponent.table.labels.noDataPlaceholderTitle', 'No data'),
body: t(
'genericcomponent.table.labels.wrongQueryError',
'Returned result is empty, there is probably an error in your query configuration. ' +
'Please, check your solution'
),
});
return;
}

const rowsDict = data.map((row) => row[resultKey]);
if (rowsDict.includes(undefined)) {
setPlaceholder({
title: t('genericcomponent.table.labels.configErrorPlaceholderTitle', 'Configuration error'),
body: t(
'genericcomponent.table.labels.wrongResultKeyError',
`Returned result doesn't have ${resultKey} property.
Probably there is an error in resultKey configuration, please, check your solution`,
{ resultKey }
),
});
return;
}

const csvRows = AgGridUtils.toCSV(rowsDict, columns);

const agGridData = _generateGridDataFromCSV(csvRows, parameterData);
if (agGridData.error) {
setClientFileDescriptor({
tableDataStatus: TABLE_DATA_STATUS.ERROR,
errors: agGridData.error,
});
} else {
setClientFileDescriptor({
name: fileName,
file: null,
agGridRows: agGridData.rows,
errors: null,
status: UPLOAD_FILE_STATUS_KEY.READY_TO_UPLOAD,
tableDataStatus: TABLE_DATA_STATUS.READY,
uploadPreprocess: { content: _uploadPreprocess },
});
}

GenericTable.downloadLocked[lockId] = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ const CategoryAccordion = (props) => {

const accordionSummary = useMemo(() => {
const categorySummary = (
<Grid container spacing={1}>
<Grid container spacing={1} data-cy={`category-accordion-summary-${category.id}`}>
<Grid item>
<Typography variant="body1">
<Typography data-cy="category-name" variant="body1">
{t(TranslationUtils.getDatasetCategoryNameTranslationKey(category.id), category.id ?? 'category')}
</Typography>
</Grid>
Expand All @@ -38,7 +38,7 @@ const CategoryAccordion = (props) => {
</Typography>
</Grid>
<Grid item>
<Typography variant="body1" className={classes.categoryType}>
<Typography data-cy="category-type" variant="body1" className={classes.categoryType}>
{t(
`commoncomponents.datasetmanager.overview.categoryTypes.${category.type.toLowerCase()}`,
category.type
Expand Down Expand Up @@ -107,7 +107,11 @@ const CategoryAccordion = (props) => {
const accordionDetails = useMemo(() => {
const hasDescription = exists(TranslationUtils.getDatasetCategoryDescriptionTranslationKey(category.id));
const descriptionString = t(TranslationUtils.getDatasetCategoryDescriptionTranslationKey(category.id));
const description = hasDescription && <Typography sx={{ whiteSpace: 'pre-line' }}>{descriptionString}</Typography>;
const description = hasDescription && (
<Typography data-cy={'category-description'} sx={{ whiteSpace: 'pre-line' }}>
{descriptionString}
</Typography>
);

const kpisWithResult = (category?.kpis ?? []).map((kpi) => ({
...kpi,
Expand Down Expand Up @@ -140,13 +144,16 @@ const CategoryAccordion = (props) => {
<Typography>{t('commoncomponents.datasetmanager.overview.attributesLabel', 'Attributes:')}</Typography>
</Grid>
<Grid item>
<Typography sx={{ opacity: '70%' }}>{category.attributes?.join(', ')}</Typography>
<Typography data-cy="category-attributes" sx={{ opacity: '70%' }}>
{category.attributes?.join(', ')}
</Typography>
</Grid>
</Grid>
);

return (
<Grid
data-cy={`category-accordion-details-${category.id}`}
container
spacing={2}
sx={{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,11 @@ export const CategoryDetailsDialog = (props) => {
return (
detailsTable && (
<>
<Button data-cy="category-details-open-button" variant="outlined" onClick={handleOpen}>
<Button data-cy="category-details-dialog-open-button" variant="outlined" onClick={handleOpen}>
{t('commoncomponents.dialog.share.dialog.buttons.open', 'Open')}
</Button>
<Dialog
data-cy="category-details-dialog"
disableEnforceFocus
fullScreen
open={open}
Expand All @@ -106,8 +107,10 @@ export const CategoryDetailsDialog = (props) => {
</Button>
<Box sx={{ flexDirection: 'column', alignItems: 'center', display: 'flex' }}>
<Box>
<Typography variant="h4">{datasetName}</Typography>
<Typography variant="h5" sx={{ opacity: '70%' }}>
<Typography data-cy="category-details-dialog-dataset-name" variant="h4">
{datasetName}
</Typography>
<Typography data-cy="category-details-dialog-category-name" variant="h5" sx={{ opacity: '70%' }}>
{t(TranslationUtils.getDatasetCategoryNameTranslationKey(category.id), category.id ?? 'category')}
</Typography>
</Box>
Expand Down
Loading

0 comments on commit 0a1f318

Please sign in to comment.