diff --git a/CHANGELOG.md b/CHANGELOG.md index d93961bc3..859c54594 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ - Change Image(sub menu) functionality added for active apps - App overview 'in review' style fix - Enhance Sub Menu by adding 'Change Description' for active apps + - Change Description(sub menu) functionality added for active apps - Bugfix - Service Release Process - Service Release process not working diff --git a/src/assets/locales/de/main.json b/src/assets/locales/de/main.json index 3625316fb..3eebe0919 100644 --- a/src/assets/locales/de/main.json +++ b/src/assets/locales/de/main.json @@ -415,6 +415,18 @@ "successMsg": "successfully changed app lead image", "errorMsg": "Unable to change the lead image" }, + "changeDescription": { + "headerTitle": "Change Descriptions", + "description": "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard .Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard .", + "longDescription": "Long Description", + "longDescriptionEN": "Long Description - EN *", + "longDescriptionDE": "Long Description - DE *", + "shortDescription": "Short Description", + "shortDescriptionEN": "Short Description - EN *", + "shortDescriptionDE": "Short Description - DE *", + "successMsg": "successfully changed description", + "errorMsg": "Unable to change description" + }, "appreleaseprocess": { "message": "App Release Process Message" }, @@ -1048,7 +1060,8 @@ }, "dataSavedSuccessMessage": "Data saved successfully", "errormessage": "Error! Something went wrong", - "fileSizeError": "Uploaded file is too big. Maximum 0.8MB is allowed" + "fileSizeError": "Uploaded file is too big. Maximum 0.8MB is allowed", + "shouldNotStartWith": "and should not start with" }, "footerButtons": { "help": "Help", diff --git a/src/assets/locales/en/main.json b/src/assets/locales/en/main.json index 32f22aafe..03be6dd92 100644 --- a/src/assets/locales/en/main.json +++ b/src/assets/locales/en/main.json @@ -414,6 +414,18 @@ "successMsg": "successfully changed app lead image", "errorMsg": "Unable to change the lead image" }, + "changeDescription": { + "headerTitle": "Change Descriptions", + "description": "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard .Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard .", + "longDescription": "Long Description", + "longDescriptionEN": "Long Description - EN *", + "longDescriptionDE": "Long Description - DE *", + "shortDescription": "Short Description", + "shortDescriptionEN": "Short Description - EN *", + "shortDescriptionDE": "Short Description - DE *", + "successMsg": "successfully changed description", + "errorMsg": "Unable to change description" + }, "appreleaseprocess": { "message": "App Release Process Message" }, @@ -1010,7 +1022,8 @@ }, "dataSavedSuccessMessage": "Data saved successfully", "errormessage": "Error! Something went wrong", - "fileSizeError": "Uploaded file is too big. Maximum 0.8MB is allowed" + "fileSizeError": "Uploaded file is too big. Maximum 0.8MB is allowed", + "shouldNotStartWith": "and should not start with" }, "footerButtons": { "help": "Help", diff --git a/src/components/pages/AppOverview/ChangeDescription.tsx b/src/components/pages/AppOverview/ChangeDescription.tsx index bcd23253f..f7f284180 100644 --- a/src/components/pages/AppOverview/ChangeDescription.tsx +++ b/src/components/pages/AppOverview/ChangeDescription.tsx @@ -17,6 +17,329 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +import { PageBreadcrumb } from 'components/shared/frame/PageBreadcrumb/PageBreadcrumb' +import { + Typography, + PageHeader, + Button, + LoadingButton, + TabPanel, +} from '@catena-x/portal-shared-components' +import { useTranslation } from 'react-i18next' +import { useLocation, useNavigate, useParams } from 'react-router-dom' +import { Box, IconButton, Tab, Tabs } from '@mui/material' +import { useEffect, useMemo, useState } from 'react' +import { error, success } from 'services/NotifyService' +import ConnectorFormInputFieldShortAndLongDescription from 'components/shared/basic/ReleaseProcess/components/ConnectorFormInputFieldShortAndLongDescription' +import Patterns from 'types/Patterns' +import { useForm } from 'react-hook-form' +import { + useFetchDescriptionQuery, + useSaveDescriptionMutation, +} from 'features/appManagement/apiSlice' +import HelpOutlineIcon from '@mui/icons-material/HelpOutline' +import TextSnippetOutlinedIcon from '@mui/icons-material/TextSnippetOutlined' + export default function ChangeDescription() { - return <>In Progress + const { t } = useTranslation() + const navigate = useNavigate() + const appId = useParams().appId + const [isLoading, setIsLoading] = useState(false) + const { state } = useLocation() + const items: any = state + const app = items?.filter((item: any) => item.id === appId) + const [activeTab, setActiveTab] = useState(0) + const longDescriptionMaxLength = 2000 + const { data: description, refetch } = useFetchDescriptionQuery(appId ?? '') + const [saveDescription] = useSaveDescriptionMutation() + + const defaultValues = useMemo(() => { + return { + longDescriptionEN: description?.[0].longDescription ?? '', + longDescriptionDE: description?.[1].longDescription ?? '', + shortDescriptionEN: description?.[0].shortDescription ?? '', + shortDescriptionDE: description?.[1].shortDescription ?? '', + } + }, [description]) + + useEffect(() => { + refetch() + reset(defaultValues) + }, [state, description]) + + const { + handleSubmit, + control, + trigger, + formState: { errors, isDirty, isValid }, + getValues, + reset, + } = useForm({ + defaultValues: defaultValues, + mode: 'onChange', + }) + + const handleSave = async (data: any) => { + setIsLoading(true) + + if (appId) { + const saveData = { + appId: appId, + body: [ + { + languageCode: 'de', + longDescription: getValues().longDescriptionDE, + shortDescription: getValues().shortDescriptionDE, + }, + { + languageCode: 'en', + longDescription: getValues().longDescriptionEN, + shortDescription: getValues().shortDescriptionEN, + }, + ], + } + + await saveDescription(saveData) + .unwrap() + .then(() => { + navigate('/appoverview', { + state: 'change-description-success', + }) + success(t('content.changeDescription.successMsg')) + }) + .catch((err) => { + setIsLoading(false) + error(t('content.changeDescription.errorMsg'), '', err) + }) + } + } + + const patternValidation = (item: string) => { + if ( + (item === 'longDescriptionEN' && + /[ @=<>*\-+#?%&_:;]/.test(getValues().longDescriptionEN?.charAt(0))) || + item === 'longDescriptionEN' + ) { + return `${t( + 'content.apprelease.appReleaseForm.validCharactersIncludes' + )} a-zA-Z0-9 !?@&#'"()[]_-+=<>/*.,;:% ${t( + 'content.apprelease.appReleaseForm.shouldNotStartWith' + )} @=<>*-+ #?%&_:;` + } else { + return `${t( + 'content.apprelease.appReleaseForm.validCharactersIncludes' + )} a-zA-ZÀ-ÿ0-9 !?@&#'"()[]_-+=<>/*.,;:% ${t( + 'content.apprelease.appReleaseForm.shouldNotStartWith' + )} @=<>*-+ #?%&_:;` + } + } + + return ( +
+ + + +
+ + {app?.[0]?.title} + + + {t('content.changeDescription.headerTitle')} + + + {t('content.changeDescription.description')} + +
+
+
+ setActiveTab(newValue)} + centered + sx={{ + '.MuiTab-root': { + textTransform: 'none', + }, + }} + > + } + id={`simple-tab-${activeTab}`} + aria-controls={`simple-tabpanel-${activeTab}`} + iconPosition="start" + /> + } + id={`simple-tab-${activeTab}`} + aria-controls={`simple-tabpanel-${activeTab}`} + iconPosition="start" + /> + + + +
+ {['longDescriptionEN', 'longDescriptionDE'].map( + (item: string) => ( +
+ + {item === 'longDescriptionEN' + ? t('content.changeDescription.longDescriptionEN') + : t('content.changeDescription.longDescriptionDE')} + + + + + } + value={ + (item === 'longDescriptionEN' + ? getValues().longDescriptionEN?.length + : getValues().longDescriptionDE?.length) + + `/${longDescriptionMaxLength}` + } + patternKey="longDescriptionEN" + patternEN={Patterns.appPage.longDescriptionEN} + patternDE={Patterns.appPage.longDescriptionDE} + rules={{ + required: + t(`content.apprelease.appPage.${item}`) + + t('content.apprelease.appReleaseForm.isMandatory'), + minLength: `${t( + 'content.apprelease.appReleaseForm.minimum' + )} 10 ${t( + 'content.apprelease.appReleaseForm.charactersRequired' + )}`, + pattern: patternValidation(item), + maxLength: `${t( + 'content.apprelease.appReleaseForm.maximum' + )} ${longDescriptionMaxLength} ${t( + 'content.apprelease.appReleaseForm.charactersAllowed' + )}`, + }} + maxLength={longDescriptionMaxLength} + /> +
+ ) + )} +
+
+ +
+ {['shortDescriptionEN', 'shortDescriptionDE'].map( + (item: string, i) => ( +
+ + {item === 'shortDescriptionEN' + ? t('content.changeDescription.shortDescriptionEN') + : t('content.changeDescription.shortDescriptionDE')} + + + + + } + value={ + (item === 'shortDescriptionEN' + ? getValues().shortDescriptionEN?.length + : getValues().shortDescriptionDE?.length) + '/255' + } + patternDE={Patterns.appMarketCard.shortDescriptionDE} + patternEN={Patterns.appMarketCard.shortDescriptionEN} + patternKey="shortDescriptionEN" + rules={{ + required: `${t( + `content.apprelease.appMarketCard.${item}` + )} ${t( + 'content.apprelease.appReleaseForm.isMandatory' + )}`, + maxLength: `${t( + 'content.apprelease.appReleaseForm.maximum' + )} 255 ${t( + 'content.apprelease.appReleaseForm.charactersAllowed' + )}`, + minLength: `${t( + 'content.apprelease.appReleaseForm.minimum' + )} 10 ${t( + 'content.apprelease.appReleaseForm.charactersRequired' + )}`, + pattern: `${t( + 'content.apprelease.appReleaseForm.validCharactersIncludes' + )} ${ + item === 'shortDescriptionEN' + ? 'a-zA-Z0-9 !?@&#\'"()_-=/*.,;:' + : 'a-zA-ZÀ-ÿ0-9 !?@&#\'"()_-=/*.,;:' + }`, + }} + /> +
+ ) + )} +
+
+
+
+
+
+ + + + + {isLoading ? ( + {}} + loading={isLoading} + label={`${t('global.actions.confirm')}`} + loadIndicator="Loading..." + /> + ) : ( + + )} + + +
+
+ ) } diff --git a/src/components/pages/AppOverview/index.tsx b/src/components/pages/AppOverview/index.tsx index 9ef36c294..9f62d1502 100644 --- a/src/components/pages/AppOverview/index.tsx +++ b/src/components/pages/AppOverview/index.tsx @@ -74,6 +74,7 @@ export default function AppOverview() { useEffect(() => { state === 'deactivate-success' && refetch() state === 'change-image-success' && refetch() + state === 'change-description-success' && refetch() }, [state, refetch]) useEffect(() => { diff --git a/src/features/appManagement/apiSlice.ts b/src/features/appManagement/apiSlice.ts index 27924f57d..3394049e5 100644 --- a/src/features/appManagement/apiSlice.ts +++ b/src/features/appManagement/apiSlice.ts @@ -196,6 +196,17 @@ export type technicalUserProfiles = { }[] } +export type saveDescriptionTypes = { + appId: string + body: descriptionTypes[] +} + +export type descriptionTypes = { + languageCode: string + longDescription: string + shortDescription: string +} + enum Tags { APP = 'App', } @@ -365,6 +376,16 @@ export const apiSlice = createApi({ }, invalidatesTags: [Tags.APP], }), + fetchDescription: builder.query({ + query: (appId) => `/api/apps/AppChange/${appId}/appupdate/description`, + }), + saveDescription: builder.mutation({ + query: (data) => ({ + url: `/api/apps/AppChange/${data.appId}/appupdate/description`, + method: 'PUT', + body: data.body, + }), + }), }), }) @@ -392,4 +413,6 @@ export const { useFetchTechnicalUserProfilesQuery, useSaveTechnicalUserProfilesMutation, useUpdateImageDataMutation, + useFetchDescriptionQuery, + useSaveDescriptionMutation, } = apiSlice