From fda5cb00faa85ee2315db6351744880e3843f38f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Synne=20Stokkev=C3=A5g=20Berg?= Date: Tue, 7 Jan 2025 16:13:33 +0100 Subject: [PATCH] Create last published component --- .../ros/src/components/riScInfo/RiScInfo.tsx | 45 ++++++-- .../riScStatus/LastPublishedComponent.tsx | 106 ++++++++++++++++++ .../riScStatus/RiScDifferenceDialog.tsx | 17 +-- .../riScStatus/RiScStatusComponent.tsx | 60 ++-------- plugins/ros/src/utils/translations.ts | 7 +- plugins/ros/src/utils/utilityfunctions.ts | 16 +++ 6 files changed, 173 insertions(+), 78 deletions(-) create mode 100644 plugins/ros/src/components/riScInfo/riScStatus/LastPublishedComponent.tsx diff --git a/plugins/ros/src/components/riScInfo/RiScInfo.tsx b/plugins/ros/src/components/riScInfo/RiScInfo.tsx index 38b4290d..03f15e57 100644 --- a/plugins/ros/src/components/riScInfo/RiScInfo.tsx +++ b/plugins/ros/src/components/riScInfo/RiScInfo.tsx @@ -1,5 +1,5 @@ -import React from 'react'; -import { RiScWithMetadata } from '../../utils/types'; +import React, { useState } from 'react'; +import { DifferenceFetchState, RiScWithMetadata } from '../../utils/types'; import { Box, Grid, Typography } from '@material-ui/core'; import { RiScStatusComponent } from './riScStatus/RiScStatusComponent'; import { InfoCard } from '@backstage/core-components'; @@ -8,25 +8,42 @@ import { pluginRiScTranslationRef } from '../../utils/translations'; import { useFontStyles } from '../../utils/style'; import EditButton from '../common/EditButton'; import { useRiScs } from '../../contexts/RiScContext'; +import { LastPublishedComponent } from './riScStatus/LastPublishedComponent'; interface RiScInfoProps { riSc: RiScWithMetadata; edit: () => void; } +const emptyDifferenceFetchState: DifferenceFetchState = { + differenceState: { + entriesOnLeft: [], + entriesOnRight: [], + difference: [], + }, + status: null, + isLoading: false, + errorMessage: '', + currentDifferenceId: '', + defaultLastModifiedDateString: '', +}; + export const RiScInfo = ({ riSc, edit }: RiScInfoProps) => { const { t } = useTranslationRef(pluginRiScTranslationRef); const { label, body2 } = useFontStyles(); const { approveRiSc } = useRiScs(); + const [differenceFetchState, setDifferenceFetchState] = + useState(emptyDifferenceFetchState); + return ( { {riSc.content.scope} - - + + + + + ); diff --git a/plugins/ros/src/components/riScInfo/riScStatus/LastPublishedComponent.tsx b/plugins/ros/src/components/riScInfo/riScStatus/LastPublishedComponent.tsx new file mode 100644 index 00000000..dc666a81 --- /dev/null +++ b/plugins/ros/src/components/riScInfo/riScStatus/LastPublishedComponent.tsx @@ -0,0 +1,106 @@ +import { parseISO } from 'date-fns'; +import { + getAgeStatus, + parseISODateFromEncryptedROS, +} from '../../../utils/utilityfunctions'; +import SentimentSatisfiedAltIcon from '@mui/icons-material/SentimentSatisfiedAlt'; +import SentimentNeutralIcon from '@mui/icons-material/SentimentNeutral'; +import SentimentVeryDissatisfiedIcon from '@mui/icons-material/SentimentVeryDissatisfied'; +import { useAuthenticatedFetch } from '../../../utils/hooks'; +import { DifferenceFetchState, RiScWithMetadata } from '../../../utils/types'; +import { Box, Typography } from '@mui/material'; +import React from 'react'; +import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { pluginRiScTranslationRef } from '../../../utils/translations'; +import { InfoCard } from '@backstage/core-components'; +import HighlightOffIcon from '@mui/icons-material/HighlightOff'; +import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'; + +interface LastPublishedComponentProps { + selectedRiSc: RiScWithMetadata; + differenceFetchState: DifferenceFetchState; + setDifferenceFetchState: React.Dispatch< + React.SetStateAction + >; + emptyDifferenceFetchState: DifferenceFetchState; +} + +export const LastPublishedComponent = ({ + selectedRiSc, + differenceFetchState, + setDifferenceFetchState, + emptyDifferenceFetchState, +}: LastPublishedComponentProps) => { + const { t } = useTranslationRef(pluginRiScTranslationRef); + + const { fetchDifference } = useAuthenticatedFetch(); + + const getDifferences = () => { + if ( + !selectedRiSc || + differenceFetchState.isLoading || + differenceFetchState.currentDifferenceId === selectedRiSc.id + ) + return; + + setDifferenceFetchState({ ...differenceFetchState, isLoading: true }); + fetchDifference( + selectedRiSc, + response => { + setDifferenceFetchState({ + differenceState: response.differenceState, + isLoading: false, + currentDifferenceId: selectedRiSc.id, + status: response.status, + errorMessage: response.errorMessage, + defaultLastModifiedDateString: response.defaultLastModifiedDateString, + }); + }, + () => { + setDifferenceFetchState({ + ...emptyDifferenceFetchState, + errorMessage: t('rosStatus.difference.error'), + status: 'FrontendFallback', // Fallback when the backend does not deliver a response with status + }); + }, + ); + }; + + const formatedDateString = parseISODateFromEncryptedROS( + differenceFetchState.defaultLastModifiedDateString, + ); + + const lastModifiedDate = + formatedDateString && parseISO(formatedDateString).toLocaleDateString(); + + getDifferences(); // TODO + + const age = lastModifiedDate && getAgeStatus(lastModifiedDate); + + return ( + + Publication status + {!lastModifiedDate && ( + + + No risk scorecard published + + )} + {lastModifiedDate && ( + + + + Risk scorecard published + + + {age === 'bad' && } + {age === 'ok' && } + {age === 'good' && } + {t('rosStatus.difference.publishDate')} + {lastModifiedDate} + + + )} + + ); +}; diff --git a/plugins/ros/src/components/riScInfo/riScStatus/RiScDifferenceDialog.tsx b/plugins/ros/src/components/riScInfo/riScStatus/RiScDifferenceDialog.tsx index 36dea00e..87ea9195 100644 --- a/plugins/ros/src/components/riScInfo/riScStatus/RiScDifferenceDialog.tsx +++ b/plugins/ros/src/components/riScInfo/riScStatus/RiScDifferenceDialog.tsx @@ -8,8 +8,6 @@ import { ErrorOutline, Favorite } from '@mui/icons-material'; import { DifferenceText } from './DifferenceText'; import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; import { pluginRiScTranslationRef } from '../../../utils/translations'; -import { parseISODateFromEncryptedROS } from '../../../utils/utilityfunctions'; -import { parseISO } from 'date-fns'; type RiScDifferenceDialogProps = { differenceFetchState: DifferenceFetchState; @@ -20,22 +18,9 @@ export const RiScDifferenceDialog = ({ }: RiScDifferenceDialogProps) => { const { t } = useTranslationRef(pluginRiScTranslationRef); - const formatedDateString = parseISODateFromEncryptedROS( - differenceFetchState.defaultLastModifiedDateString, - ); - - const parsedDateString = formatedDateString - ? parseISO(formatedDateString).toLocaleDateString() - : null; return ( - {t('rosStatus.difference.description')} - - {parsedDateString && - t('rosStatus.difference.publishDate', { - date: parsedDateString, - })} - + {t('rosStatus.difference.description')} {t('rosStatus.difference.differences.title')} diff --git a/plugins/ros/src/components/riScInfo/riScStatus/RiScStatusComponent.tsx b/plugins/ros/src/components/riScInfo/riScStatus/RiScStatusComponent.tsx index 183c4050..b1b76568 100644 --- a/plugins/ros/src/components/riScInfo/riScStatus/RiScStatusComponent.tsx +++ b/plugins/ros/src/components/riScInfo/riScStatus/RiScStatusComponent.tsx @@ -14,7 +14,6 @@ import { useRiScs } from '../../../contexts/RiScContext'; import { subtitle1 } from '../../common/typography'; import Box from '@mui/material/Box'; import { WarningAmberOutlined } from '@mui/icons-material'; -import { useAuthenticatedFetch } from '../../../utils/hooks'; import Progress from './Progress'; import { RiScMigrationDialog } from '../MigrationDialog'; import { RiScPublishDialog } from '../PublishDialog'; @@ -22,30 +21,24 @@ import PendingActionsIcon from '@mui/icons-material/PendingActions'; import EditNoteIcon from '@mui/icons-material/EditNote'; import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'; -const emptyDifferenceFetchState: DifferenceFetchState = { - differenceState: { - entriesOnLeft: [], - entriesOnRight: [], - difference: [], - }, - status: null, - isLoading: false, - errorMessage: '', - currentDifferenceId: '', - defaultLastModifiedDateString: '', -}; - interface RiScStatusProps { selectedRiSc: RiScWithMetadata; publishRiScFn: () => void; + differenceFetchState: DifferenceFetchState; + setDifferenceFetchState: React.Dispatch< + React.SetStateAction + >; + emptyDifferenceFetchState: DifferenceFetchState; } export const RiScStatusComponent = ({ selectedRiSc, publishRiScFn, + differenceFetchState, + setDifferenceFetchState, + emptyDifferenceFetchState, }: RiScStatusProps) => { const { t } = useTranslationRef(pluginRiScTranslationRef); - const { fetchDifference } = useAuthenticatedFetch(); const [publishRiScDialogIsOpen, setPublishRiScDialogIsOpen] = useState(false); @@ -53,9 +46,6 @@ export const RiScStatusComponent = ({ const [migrationDialogIsOpen, setMigrationDialogIsOpen] = useState(false); - const [differenceFetchState, setDifferenceFetchState] = - useState(emptyDifferenceFetchState); - const { updateRiSc } = useRiScs(); const handleApproveAndPublish = () => { @@ -63,37 +53,6 @@ export const RiScStatusComponent = ({ setPublishRiScDialogIsOpen(false); }; - const getDifferences = () => { - if ( - !selectedRiSc || - differenceFetchState.isLoading || - differenceFetchState.currentDifferenceId === selectedRiSc.id - ) - return; - - setDifferenceFetchState({ ...differenceFetchState, isLoading: true }); - fetchDifference( - selectedRiSc, - response => { - setDifferenceFetchState({ - differenceState: response.differenceState, - isLoading: false, - currentDifferenceId: selectedRiSc.id, - status: response.status, - errorMessage: response.errorMessage, - defaultLastModifiedDateString: response.defaultLastModifiedDateString, - }); - }, - () => { - setDifferenceFetchState({ - ...emptyDifferenceFetchState, - errorMessage: t('rosStatus.difference.error'), - status: 'FrontendFallback', // Fallback when the backend does not deliver a response with status - }); - }, - ); - }; - const handleUpdate = () => { updateRiSc(selectedRiSc.content); setMigrationDialogIsOpen(false); @@ -101,12 +60,11 @@ export const RiScStatusComponent = ({ const handleOpenPublishRiScDialog = () => { setPublishRiScDialogIsOpen(true); - getDifferences(); }; useEffect(() => { setDifferenceFetchState(emptyDifferenceFetchState); - }, [selectedRiSc]); + }, [emptyDifferenceFetchState, selectedRiSc, setDifferenceFetchState]); const [status, setStatus] = useState<0 | 1 | 2 | 3>(0); diff --git a/plugins/ros/src/utils/translations.ts b/plugins/ros/src/utils/translations.ts index 8eb37665..0d87c4d8 100644 --- a/plugins/ros/src/utils/translations.ts +++ b/plugins/ros/src/utils/translations.ts @@ -69,7 +69,7 @@ export const pluginRiScTranslationRef = createTranslationRef({ rosStatus: { statusBadge: { missing: - 'Once the draft is complete, the risk owner can review and accept the changes.', // Mangler godkjenning av risikoeier + 'Once the draft is complete, the risk owner can accept the changes.', // Mangler godkjenning av risikoeier approved: 'Accepted by risk owner', // Godkjent av risikoeier error: 'Failed to retrieve status', // Kunne ikke hente status migration: { @@ -84,7 +84,7 @@ export const pluginRiScTranslationRef = createTranslationRef({ }, difference: { description: 'Summary of changes that will be approved by risk owner.', - publishDate: 'Last published changes {{date}}', + publishDate: 'Last published changes: ', fetching: 'Fetching changes', error: 'Error while fetching changes', newROS: 'No published Risk scorecards to compare with', @@ -514,8 +514,7 @@ export const pluginRiScNorwegianTranslation = createTranslationResource({ 'rosStatus.moreInformationButton': 'Mer informasjon', 'rosStatus.difference.description': 'Oppsummering av endringer som må godkjennes av risikoeier.', - 'rosStatus.difference.publishDate': - 'Siste publiserte endringer {{date}}', + 'rosStatus.difference.publishDate': 'Siste publiserte endringer: ', 'rosStatus.difference.fetching': 'Henter endringer', 'rosStatus.difference.error': 'Feil med uthenting av endringer', 'rosStatus.difference.newROS': diff --git a/plugins/ros/src/utils/utilityfunctions.ts b/plugins/ros/src/utils/utilityfunctions.ts index a6c612e7..4c053a42 100644 --- a/plugins/ros/src/utils/utilityfunctions.ts +++ b/plugins/ros/src/utils/utilityfunctions.ts @@ -316,3 +316,19 @@ export const deleteAction = ( remove(index); onSubmit(); }; + +export const getAgeStatus = (date: string): 'good' | 'ok' | 'bad' => { + const [day, month, year] = date.split('/').map(Number); + const parsedDate: Date = new Date(year, month - 1, day); + + const now: Date = new Date(); + const diffInMilliseconds: number = now.getTime() - parsedDate.getTime(); + const diffInMonths: number = diffInMilliseconds / (1000 * 60 * 60 * 24 * 30); + + if (diffInMonths > 6) { + return 'bad'; + } else if (diffInMonths > 3) { + return 'ok'; + } + return 'good'; +};