fix(ui): fqn encoding issue on customizable landing page (#13577)

* Fixed the issue with the encoded fqn passed to the layout data on customizable landing page
Added success and fail toasts after saving the layouts

* localization changes for other languages

* Added missing widget images

* fixed code smells
This commit is contained in:
Aniket Katkar 2023-10-16 16:21:41 +05:30 committed by GitHub
parent 6869fc04d7
commit d6be186958
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 108 additions and 26 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -17,4 +17,5 @@ export interface CustomizeMyDataProps {
initialPageData: Document;
onSaveLayout: () => Promise<void>;
handlePageDataChange: (newPageData: Document) => void;
handleSaveCurrentPageLayout: (value: boolean) => void;
}

View File

@ -61,6 +61,7 @@ function CustomizeMyData({
initialPageData,
onSaveLayout,
handlePageDataChange,
handleSaveCurrentPageLayout,
}: Readonly<CustomizeMyDataProps>) {
const { t } = useTranslation();
const history = useHistory();
@ -210,6 +211,7 @@ function CustomizeMyData({
followedData={followedData ?? []}
followedDataCount={followedDataCount}
handleResetLayout={handleResetRightPanelLayout}
handleSaveCurrentPageLayout={handleSaveCurrentPageLayout}
isAnnouncementLoading={isAnnouncementLoading}
isLoadingOwnedData={isLoadingOwnedData}
layoutConfigData={widgetConfig.data}
@ -318,11 +320,6 @@ function CustomizeMyData({
);
}, []);
const handleSave = useCallback(async () => {
await onSaveLayout();
handleCancel();
}, [onSaveLayout]);
const handleReset = useCallback(() => {
setLayout([
...customizePageClassBase.landingPageDefaultLayout,
@ -377,7 +374,7 @@ function CustomizeMyData({
<Button size="small" onClick={handleOpenResetModal}>
{t('label.reset')}
</Button>
<Button size="small" type="primary" onClick={handleSave}>
<Button size="small" type="primary" onClick={onSaveLayout}>
{t('label.save')}
</Button>
</Space>

View File

@ -38,7 +38,7 @@ function AnnouncementsWidget({
}, [widgetKey]);
return (
<div className="bg-white h-max-full overflow-y-scroll">
<div className="bg-white h-full h-max-full overflow-y-scroll no-scrollbar">
<Row justify="space-between">
<Col>
<Typography.Paragraph className="right-panel-label m-b-sm">

View File

@ -42,6 +42,7 @@ const RightSidebar = ({
updateParentLayout,
resetLayout = false,
handleResetLayout,
handleSaveCurrentPageLayout,
}: RightSidebarProps) => {
const [layout, setLayout] = useState<Array<WidgetConfig>>([
...(layoutConfigData?.page?.layout ?? []),
@ -205,7 +206,7 @@ const RightSidebar = ({
}, [layout]);
useEffect(() => {
if (resetLayout && handleResetLayout) {
if (resetLayout && handleResetLayout && handleSaveCurrentPageLayout) {
setLayout([
...customizePageClassBase.rightPanelDefaultLayout,
...(isEditView
@ -222,6 +223,7 @@ const RightSidebar = ({
: []),
]);
handleResetLayout(false);
handleSaveCurrentPageLayout(true);
}
}, [resetLayout]);

View File

@ -27,4 +27,5 @@ export interface RightSidebarProps {
layoutConfigData?: WidgetConfig['data'];
parentLayoutData?: Array<WidgetConfig>;
updateParentLayout?: (newLayout: Array<WidgetConfig>) => void;
handleSaveCurrentPageLayout?: (value: boolean) => void;
}

View File

@ -195,6 +195,7 @@
"created-date": "Erstellungsdatum",
"created-lowercase": "erstellt",
"creating-account": "Konto erstellen",
"creating-lowercase": "creating",
"credentials-type": "Anmeldeinformationstyp",
"criteria": "Kriterien",
"cron": "Cron",
@ -1070,6 +1071,7 @@
"updated-by": "Aktualisiert von",
"updated-lowercase": "aktualisiert",
"updated-on": "Aktualisiert am",
"updating-lowercase": "updating",
"upload-csv-uppercase-file": "CSV-Datei hochladen",
"upstream-depth": "Upstream-Tiefe",
"url-lowercase": "URL",
@ -1615,6 +1617,8 @@
"no-query-available": "Keine Abfrage verfügbar.",
"no-task-available": "Keine Aufgabendaten verfügbar.",
"no-task-creation-without-assignee": "Es kann keine Aufgabe ohne Beauftragten erstellt werden.",
"page-layout-operation-error": "Error while {{operation}} the page layout.",
"page-layout-operation-success": "Page layout {{operation}} successfully.",
"please-add-description": "Eine leere Beschreibung wird nicht akzeptiert. Bitte fügen Sie eine Beschreibung hinzu.",
"please-add-tags": "Eine leere Tag-Liste wird nicht akzeptiert. Bitte fügen Sie einen Tag hinzu.",
"re-indexing-error": "Fehler beim Re-Indexieren!",

View File

@ -195,6 +195,7 @@
"created-date": "Created Date",
"created-lowercase": "created",
"creating-account": "Creating Account",
"creating-lowercase": "creating",
"credentials-type": "Credentials Type",
"criteria": "Criteria",
"cron": "Cron",
@ -1070,6 +1071,7 @@
"updated-by": "Updated by",
"updated-lowercase": "updated",
"updated-on": "Updated on",
"updating-lowercase": "updating",
"upload-csv-uppercase-file": "Upload CSV file",
"upstream-depth": "Upstream Depth",
"url-lowercase": "url",
@ -1615,6 +1617,8 @@
"no-query-available": "No query available.",
"no-task-available": "No task data is available.",
"no-task-creation-without-assignee": "Cannot create a task without assignee",
"page-layout-operation-error": "Error while {{operation}} the page layout.",
"page-layout-operation-success": "Page layout {{operation}} successfully.",
"please-add-description": "Empty description is not accepted. Please add a description.",
"please-add-tags": "An empty tag list is not accepted. Please add a tag.",
"re-indexing-error": "Error while re indexing!",

View File

@ -195,6 +195,7 @@
"created-date": "Created Date",
"created-lowercase": "creado",
"creating-account": "Creando cuenta",
"creating-lowercase": "creating",
"credentials-type": "Tipo de credenciales",
"criteria": "Criterio",
"cron": "Cron",
@ -1070,6 +1071,7 @@
"updated-by": "Actualizado por",
"updated-lowercase": "actualizado",
"updated-on": "Actualizado el",
"updating-lowercase": "updating",
"upload-csv-uppercase-file": "Cargar archivo CSV",
"upstream-depth": "Profundidad aguas arriba",
"url-lowercase": "url",
@ -1615,6 +1617,8 @@
"no-query-available": "No hay consulta disponible.",
"no-task-available": "No hay datos de tarea disponibles.",
"no-task-creation-without-assignee": "No se puede crear una tarea sin destinatario",
"page-layout-operation-error": "Error while {{operation}} the page layout.",
"page-layout-operation-success": "Page layout {{operation}} successfully.",
"please-add-description": "No se acepta una descripción vacía. Por favor agregue una descripción.",
"please-add-tags": "No se acepta una lista de etiquetas vacía. Por favor agregue una etiqueta.",
"re-indexing-error": "Error while re indexing!",

View File

@ -195,6 +195,7 @@
"created-date": "Date de Création",
"created-lowercase": "créé",
"creating-account": "Création du Compte",
"creating-lowercase": "creating",
"credentials-type": "Type d'Identifiants",
"criteria": "Critères",
"cron": "Cron",
@ -1070,6 +1071,7 @@
"updated-by": "Mis à Jour par",
"updated-lowercase": "mis à jour",
"updated-on": "Mis à Jour le",
"updating-lowercase": "updating",
"upload-csv-uppercase-file": "Télécharger le Fichier CSV",
"upstream-depth": "Profondeur de l'Amont",
"url-lowercase": "url",
@ -1615,6 +1617,8 @@
"no-query-available": "Aucune requête disponible",
"no-task-available": "Aucune donnée n'est disponible pour les tâches",
"no-task-creation-without-assignee": "Impossible de créer une tâche sans destinataire",
"page-layout-operation-error": "Error while {{operation}} the page layout.",
"page-layout-operation-success": "Page layout {{operation}} successfully.",
"please-add-description": "La description vide n'est pas acceptée. Veuillez ajouter une description.",
"please-add-tags": "Impossible d'accepter une liste de tags vide. Veuillez ajouter un tag.",
"re-indexing-error": "Erreur lors du réindexage !",

View File

@ -195,6 +195,7 @@
"created-date": "Created Date",
"created-lowercase": "作成済",
"creating-account": "アカウントの作成",
"creating-lowercase": "creating",
"credentials-type": "Credentials Type",
"criteria": "クライテリア",
"cron": "Cron",
@ -1070,6 +1071,7 @@
"updated-by": "Updated by",
"updated-lowercase": "updated",
"updated-on": "Updated on",
"updating-lowercase": "updating",
"upload-csv-uppercase-file": "CSVファイルをアップロード",
"upstream-depth": "Upstream Depth",
"url-lowercase": "url",
@ -1615,6 +1617,8 @@
"no-query-available": "利用可能なクエリはありません。",
"no-task-available": "タスク情報はありません。",
"no-task-creation-without-assignee": "担当者のいないタスクは作成できません",
"page-layout-operation-error": "Error while {{operation}} the page layout.",
"page-layout-operation-success": "Page layout {{operation}} successfully.",
"please-add-description": "空の説明は受け付けられません。説明を追加してください。",
"please-add-tags": "空のタグリストは受け付けられません。タグを追加してください。",
"re-indexing-error": "Error while re indexing!",

View File

@ -195,6 +195,7 @@
"created-date": "Created Date",
"created-lowercase": "criado",
"creating-account": "Criando conta",
"creating-lowercase": "creating",
"credentials-type": "Tipo de credenciais",
"criteria": "Critério",
"cron": "Cron",
@ -1070,6 +1071,7 @@
"updated-by": "Atualizado por",
"updated-lowercase": "atualizado",
"updated-on": "Atualizado em",
"updating-lowercase": "updating",
"upload-csv-uppercase-file": "Enviar arquivo CSV",
"upstream-depth": "Profundidade a montante",
"url-lowercase": "url",
@ -1615,6 +1617,8 @@
"no-query-available": "Nenhuma consulta disponível.",
"no-task-available": "Não há dados de tarefa disponíveis.",
"no-task-creation-without-assignee": "Não é possível criar uma tarefa sem destinatário",
"page-layout-operation-error": "Error while {{operation}} the page layout.",
"page-layout-operation-success": "Page layout {{operation}} successfully.",
"please-add-description": "Descrição vazia não é aceita. Por favor, adicione uma descrição.",
"please-add-tags": "Uma lista de tags vazia não é aceita. Por favor, adicione uma tag.",
"re-indexing-error": "Error while re indexing!",

View File

@ -195,6 +195,7 @@
"created-date": "Дата создания",
"created-lowercase": "создано",
"creating-account": "Создание аккаунта",
"creating-lowercase": "creating",
"credentials-type": "Тип учетных данных",
"criteria": "Критерий",
"cron": "Крон",
@ -1070,6 +1071,7 @@
"updated-by": "Обновлено",
"updated-lowercase": "обновлено",
"updated-on": "Обновление",
"updating-lowercase": "updating",
"upload-csv-uppercase-file": "Загрузить CSV-файл",
"upstream-depth": "Восходящая линия",
"url-lowercase": "url",
@ -1615,6 +1617,8 @@
"no-query-available": "Нет доступных запросов.",
"no-task-available": "Данных о задании нет.",
"no-task-creation-without-assignee": "Невозможно создать задачу без исполнителя",
"page-layout-operation-error": "Error while {{operation}} the page layout.",
"page-layout-operation-success": "Page layout {{operation}} successfully.",
"please-add-description": "Пустое описание не принимается. Пожалуйста, добавьте описание.",
"please-add-tags": "Пустой список тегов не принимается. Пожалуйста, добавьте тег.",
"re-indexing-error": "Ошибка при переиндексации!",

View File

@ -195,6 +195,7 @@
"created-date": "创建日期",
"created-lowercase": "创建",
"creating-account": "注册帐号",
"creating-lowercase": "creating",
"credentials-type": "凭证类型",
"criteria": "标准",
"cron": "Cron",
@ -1070,6 +1071,7 @@
"updated-by": "更新者",
"updated-lowercase": "已更新",
"updated-on": "更新于",
"updating-lowercase": "updating",
"upload-csv-uppercase-file": "上传 CSV 文件",
"upstream-depth": "上游深度",
"url-lowercase": "url",
@ -1615,6 +1617,8 @@
"no-query-available": "没有可用的查询",
"no-task-available": "没有任务数据可用",
"no-task-creation-without-assignee": "无法创建没有受派人的任务",
"page-layout-operation-error": "Error while {{operation}} the page layout.",
"page-layout-operation-success": "Page layout {{operation}} successfully.",
"please-add-description": "不接受空描述,请添加描述信息",
"please-add-tags": "不接受空标签列表,请添加标签信息",
"re-indexing-error": "在重新索引时发生错误!",

View File

@ -12,7 +12,8 @@
*/
import { AxiosError } from 'axios';
import { compare } from 'fast-json-patch';
import React, { useCallback, useEffect, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
import CustomizeMyData from '../../components/CustomizableComponents/CustomizeMyData/CustomizeMyData';
import Loader from '../../components/Loader/Loader';
@ -27,22 +28,37 @@ import {
} from '../../rest/DocStoreAPI';
import { getFinalLandingPage } from '../../utils/CustomizableLandingPageUtils';
import customizePageClassBase from '../../utils/CustomizePageClassBase';
import { getDecodedFqn } from '../../utils/StringsUtils';
import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils';
export const CustomizablePage = () => {
const { fqn, pageFqn } = useParams<{ fqn: string; pageFqn: PageType }>();
const { fqn: personaFQN, pageFqn } =
useParams<{ fqn: string; pageFqn: PageType }>();
const { t } = useTranslation();
const [page, setPage] = useState<Document>({} as Document);
const [editedPage, setEditedPage] = useState<Document>({} as Document);
const [isLoading, setIsLoading] = useState(true);
const [saveCurrentPageLayout, setSaveCurrentPageLayout] = useState(false);
const decodedPersonaFQN = useMemo(
() => getDecodedFqn(personaFQN),
[personaFQN]
);
const decodedPageFQN = useMemo(() => getDecodedFqn(pageFqn), [pageFqn]);
const handlePageDataChange = useCallback((newPageData: Document) => {
setEditedPage(newPageData);
}, []);
const handleSaveCurrentPageLayout = useCallback((value: boolean) => {
setSaveCurrentPageLayout(value);
}, []);
const fetchDocument = async () => {
const pageFQN = `${EntityType.PERSONA}.${fqn}.${EntityType.PAGE}.${pageFqn}`;
const pageLayoutFQN = `${EntityType.PERSONA}.${personaFQN}.${EntityType.PAGE}.${pageFqn}`;
try {
setIsLoading(true);
const pageData = await getDocumentByFQN(pageFQN);
const pageData = await getDocumentByFQN(pageLayoutFQN);
const finalPageData = getFinalLandingPage(pageData, true);
setPage(finalPageData);
@ -52,8 +68,8 @@ export const CustomizablePage = () => {
setPage(
getFinalLandingPage(
{
name: `${fqn}${pageFqn}`,
fullyQualifiedName: pageFQN,
name: `${decodedPersonaFQN}${decodedPageFQN}`,
fullyQualifiedName: getDecodedFqn(pageLayoutFQN),
entityType: EntityType.PAGE,
data: {
page: {
@ -73,22 +89,46 @@ export const CustomizablePage = () => {
const handleSave = async () => {
try {
const finalPage = getFinalLandingPage(editedPage);
let response: Document;
if (page.id) {
const jsonPatch = compare(page, finalPage);
await updateDocument(page?.id ?? '', jsonPatch);
response = await updateDocument(page.id ?? '', jsonPatch);
} else {
await createDocument(finalPage);
response = await createDocument(finalPage);
}
setPage(response);
setEditedPage(response);
showSuccessToast(
t('server.page-layout-operation-success', {
operation: page.id
? t('label.updated-lowercase')
: t('label.created-lowercase'),
})
);
} catch {
// Error
showErrorToast(
t('server.page-layout-operation-error', {
operation: page.id
? t('label.updating-lowercase')
: t('label.creating-lowercase'),
})
);
}
};
useEffect(() => {
if (saveCurrentPageLayout) {
handleSave();
setSaveCurrentPageLayout(false);
}
}, [saveCurrentPageLayout]);
useEffect(() => {
fetchDocument();
}, [fqn, pageFqn]);
}, [personaFQN, pageFqn]);
if (isLoading) {
return <Loader />;
@ -98,6 +138,7 @@ export const CustomizablePage = () => {
return (
<CustomizeMyData
handlePageDataChange={handlePageDataChange}
handleSaveCurrentPageLayout={handleSaveCurrentPageLayout}
initialPageData={page}
onSaveLayout={handleSave}
/>

View File

@ -49,11 +49,10 @@ export const updateDocument = async (id: string, data: Operation[]) => {
const configOptions = {
headers: { 'Content-type': 'application/json-patch+json' },
};
const response = await axiosClient.patch(
`${BASE_URL}/${id}`,
data,
configOptions
);
const response = await axiosClient.patch<
Operation[],
AxiosResponse<Document>
>(`${BASE_URL}/${id}`, data, configOptions);
return response.data;
};

View File

@ -501,7 +501,7 @@ a[href].link-text-grey,
.no-scrollbar {
scrollbar-width: none;
::-webkit-scrollbar {
&::-webkit-scrollbar {
display: none;
}
}

View File

@ -51,7 +51,7 @@ export const getAddWidgetHandler =
placeholderWidgetKey === LandingPageWidgetKeys.EMPTY_WIDGET_PLACEHOLDER
) {
return [
...currentLayout,
...moveEmptyWidgetToTheEnd(currentLayout),
{
w: widgetWidth,
h: widgetHeight,
@ -95,6 +95,13 @@ const getEmptyWidgetHeight = (
}
};
export const moveEmptyWidgetToTheEnd = (layout: Array<WidgetConfig>) =>
layout.map((widget) =>
widget.i === LandingPageWidgetKeys.EMPTY_WIDGET_PLACEHOLDER
? { ...widget, y: 100 }
: widget
);
export const getRemoveWidgetHandler =
(widgetKey: string, minHeight: number, maxHeight: number) =>
(currentLayout: Array<WidgetConfig>) => {

View File

@ -15,9 +15,11 @@ import { FC } from 'react';
import ActivityFeedImg from '../assets/img/activity-feed.png';
import AnnouncementImg from '../assets/img/announcement.png';
import FollowingImg from '../assets/img/following.png';
import KPISmallImg from '../assets/img/kpi-small.png';
import KPIImg from '../assets/img/kpi.png';
import MyDataImg from '../assets/img/my-data.png';
import RecentViewsImg from '../assets/img/recent-views.png';
import TotalAssetsMediumImg from '../assets/img/total-assets-medium.png';
import TotalAssetsImg from '../assets/img/total-assets.png';
import KPIWidget from '../components/KPIWidget/KPIWidget.component';
import { MyDataWidget } from '../components/MyData/MyDataWidget/MyDataWidget.component';
@ -198,14 +200,14 @@ class CustomizePageClassBase {
}
case LandingPageWidgetKeys.KPI: {
if (size === WidgetWidths.small) {
return '';
return KPISmallImg;
}
return KPIImg;
}
case LandingPageWidgetKeys.TOTAL_DATA_ASSETS: {
if (size === WidgetWidths.medium) {
return '';
return TotalAssetsMediumImg;
}
return TotalAssetsImg;