From 338320bc7ad0bc47946f12a729d1138772a1d3e5 Mon Sep 17 00:00:00 2001 From: Mateusz Bacherycz Date: Thu, 27 Feb 2025 15:25:14 +0100 Subject: [PATCH] chore: Exchange gui actions with AC DialogActions component --- packages/frontend/src/api/useDialogById.tsx | 4 +- .../src/components/InboxItem/GuiActions.tsx | 129 ------------------ .../components/InboxItem/InboxItemDetail.tsx | 69 +++++++++- .../InboxItem/guiActions.module.css | 12 -- .../src/components/InboxItem/index.ts | 1 - 5 files changed, 68 insertions(+), 147 deletions(-) delete mode 100644 packages/frontend/src/components/InboxItem/GuiActions.tsx delete mode 100644 packages/frontend/src/components/InboxItem/guiActions.module.css diff --git a/packages/frontend/src/api/useDialogById.tsx b/packages/frontend/src/api/useDialogById.tsx index 8b5b2d78..12ca2f0c 100644 --- a/packages/frontend/src/api/useDialogById.tsx +++ b/packages/frontend/src/api/useDialogById.tsx @@ -14,7 +14,7 @@ import { } from 'bff-types-generated'; import { AttachmentUrlConsumer } from 'bff-types-generated'; import { t } from 'i18next'; -import type { GuiActionButtonProps } from '../components'; +import type { DialogActionProps } from '../components/InboxItem/InboxItemDetail.tsx'; import { QUERY_KEYS } from '../constants/queryKeys.ts'; import { type ValueType, getPreferredPropertyByLocale } from '../i18n/property.ts'; import { useOrganizations } from '../pages/Inbox/useOrganizations.ts'; @@ -64,7 +64,7 @@ export interface DialogByIdDetails { sender: Participant; receiver: Participant; title: string; - guiActions: GuiActionButtonProps[]; + guiActions: DialogActionProps[]; additionalInfo: { value: string; mediaType: string } | undefined; attachments: AttachmentFieldsFragment[]; dialogToken: string; diff --git a/packages/frontend/src/components/InboxItem/GuiActions.tsx b/packages/frontend/src/components/InboxItem/GuiActions.tsx deleted file mode 100644 index 6edfe0ad..00000000 --- a/packages/frontend/src/components/InboxItem/GuiActions.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import { Button, Spinner } from '@digdir/designsystemet-react'; -import type { GuiActionPriority } from 'bff-types-generated'; -import { type ReactElement, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import styles from './guiActions.module.css'; -export interface GuiActionProps { - actions: GuiActionButtonProps[]; - dialogToken: string; -} - -export interface GuiActionButtonProps { - id: string; - url: string; - priority: GuiActionPriority; - isDeleteAction: boolean; - httpMethod: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'TRACE' | 'CONNECT'; - title: string; - prompt?: string; - disabled?: boolean; -} - -interface GuiActionsProps { - actions: GuiActionButtonProps; - dialogToken: string; -} - -/** - * Handles the button click event based on HTTP method. - * - * @param {GuiActionButton} props - The properties passed to the button component. - * @param dialogToken - The dialog token used for authorization. - * @param responseFinished - */ -const handleButtonClick = async (props: GuiActionButtonProps, dialogToken: string, responseFinished: () => void) => { - const { url, httpMethod, prompt } = props; - - if (prompt && !window.confirm(prompt)) { - responseFinished(); - return; - } - - if (httpMethod === 'GET') { - responseFinished(); - window.open(url, '_blank'); - } else { - try { - const response = await fetch(url, { - method: httpMethod, - headers: { - Authorization: `Bearer ${dialogToken}`, - }, - }); - - if (!response.ok) { - console.error(`Error: ${response.statusText}`); - } - } catch (error) { - console.error('Error performing action:', error); - } finally { - responseFinished(); - } - } -}; - -/** - * Renders a single action button with the specified properties. - * - * @component - * @param {GuiActionButton} props - The properties passed to the button component. - * @returns {ReactElement | null} The rendered button or null if hidden. - */ -const GuiActionButton = ({ actions, dialogToken }: GuiActionsProps): ReactElement | null => { - const { t } = useTranslation(); - const [isLoading, setIsLoading] = useState(false); - - const { priority, id, title, disabled = false } = actions; - const variant = priority.toLowerCase() as 'primary' | 'secondary' | 'tertiary'; - - const responseCallback = () => { - setIsLoading(false); - }; - - const handleClick = () => { - setIsLoading(true); - void handleButtonClick(actions, dialogToken, responseCallback); - }; - - if (isLoading) { - return ( - - ); - } - - return ( - - ); -}; - -/** - * Renders a list of action buttons based on the provided actions array. - * - * @component - * @param {GuiActionProps} props - The properties passed to the component. - * @returns {ReactElement} The container with all rendered action buttons. - * - * @example - * console.log('Deleted')} - * actions={[ - * { id: 'btn1', url: '/submit', disabled: false, priority: 'Primary', httpMethod: 'POST', title: 'Submit', dialogToken: 'token123' }, - * { id: 'btn2', url: '/cancel', disabled: false, priority: 'Secondary', httpMethod: 'GET', title: 'Cancel', dialogToken: 'token123' }, - * ]} - * dialogToken="your-dialog-token" - * /> - */ -export const GuiActions = ({ actions, dialogToken }: GuiActionProps): ReactElement => { - return ( -
- {actions.map((actionProps) => ( - - ))} -
- ); -}; diff --git a/packages/frontend/src/components/InboxItem/InboxItemDetail.tsx b/packages/frontend/src/components/InboxItem/InboxItemDetail.tsx index d983fece..a9bdb9c3 100644 --- a/packages/frontend/src/components/InboxItem/InboxItemDetail.tsx +++ b/packages/frontend/src/components/InboxItem/InboxItemDetail.tsx @@ -1,11 +1,14 @@ import { Article, type AttachmentLinkProps, + type DialogActionButtonProps, + DialogActions, DialogAttachments, DialogBody, + type DialogButtonPriority, DialogHeader, } from '@altinn/altinn-components'; -import type { ReactElement } from 'react'; +import { type ReactElement, useState } from 'react'; import { useTranslation } from 'react-i18next'; import type { DialogActivity, DialogByIdDetails, DialogTransmission } from '../../api/useDialogById.tsx'; import { getPreferredPropertyByLocale } from '../../i18n/property.ts'; @@ -15,7 +18,6 @@ import { Activity } from '../Activity'; import { AdditionalInfoContent } from '../AdditonalInfoContent'; import { MainContentReference } from '../MainContentReference'; import { Transmission } from '../Transmission'; -import { GuiActions } from './GuiActions.tsx'; import styles from './inboxItemDetail.module.css'; interface InboxItemDetailProps { @@ -47,6 +49,51 @@ interface InboxItemDetailProps { * /> */ +export interface DialogActionProps { + id: string; + title: string; + url: string; + httpMethod: string; + prompt?: string; + disabled?: boolean; + priority: string; +} + +const handleDialogActionClick = async ( + props: DialogActionProps, + dialogToken: string, + responseFinished: () => void, +): Promise => { + const { url, httpMethod, prompt } = props; + + if (prompt && !window.confirm(prompt)) { + responseFinished(); + return; + } + + if (httpMethod === 'GET') { + responseFinished(); + window.open(url, '_blank'); + } else { + try { + const response = await fetch(url, { + method: httpMethod, + headers: { + Authorization: `Bearer ${dialogToken}`, + }, + }); + + if (!response.ok) { + console.error(`Error: ${response.statusText}`); + } + } catch (error) { + console.error('Error performing action:', error); + } finally { + responseFinished(); + } + } +}; + export const InboxItemDetail = ({ dialog }: InboxItemDetailProps): ReactElement => { const { t } = useTranslation(); const format = useFormat(); @@ -95,6 +142,22 @@ export const InboxItemDetail = ({ dialog }: InboxItemDetailProps): ReactElement const formatString = clockPrefix ? `do MMMM yyyy '${clockPrefix}' HH.mm` : `do MMMM yyyy HH.mm`; const dueAtLabel = dueAt ? format(dueAt, formatString) : ''; + const [isLoading, setIsLoading] = useState(''); + + const dialogActions: DialogActionButtonProps[] = guiActions.map((action) => ({ + id: action.id, + label: action.title, + disabled: !!isLoading || action.disabled, + priority: action.priority.toLocaleLowerCase() as DialogButtonPriority, + url: action.url, + httpMethod: action.httpMethod, + loading: isLoading === action.id, + onClick: () => { + setIsLoading(action.id); + handleDialogActionClick(action, dialogToken, () => setIsLoading('')); + }, + })); + return (
@@ -114,7 +177,7 @@ export const InboxItemDetail = ({ dialog }: InboxItemDetailProps): ReactElement items={attachmentItems} /> )} - {guiActions.length > 0 && } + {activities.length > 0 && ( diff --git a/packages/frontend/src/components/InboxItem/guiActions.module.css b/packages/frontend/src/components/InboxItem/guiActions.module.css deleted file mode 100644 index 278ad65b..00000000 --- a/packages/frontend/src/components/InboxItem/guiActions.module.css +++ /dev/null @@ -1,12 +0,0 @@ -.guiActions { - display: flex; - align-items: center; - column-gap: 0.5rem; - row-gap: 0.5rem; - flex-wrap: wrap; - padding: 1rem 0; -} - -.button { - border: none; -} diff --git a/packages/frontend/src/components/InboxItem/index.ts b/packages/frontend/src/components/InboxItem/index.ts index 4b32de50..0b357be6 100644 --- a/packages/frontend/src/components/InboxItem/index.ts +++ b/packages/frontend/src/components/InboxItem/index.ts @@ -1,3 +1,2 @@ export * from './InboxItems.tsx'; export * from './InboxItemDetail.tsx'; -export * from './GuiActions.tsx';