diff --git a/opencti-platform/opencti-front/src/private/components/settings/sub_types/fintel_templates/FintelTemplate.tsx b/opencti-platform/opencti-front/src/private/components/settings/sub_types/fintel_templates/FintelTemplate.tsx
index 2c796140cb003..9d3b00e18957b 100644
--- a/opencti-platform/opencti-front/src/private/components/settings/sub_types/fintel_templates/FintelTemplate.tsx
+++ b/opencti-platform/opencti-front/src/private/components/settings/sub_types/fintel_templates/FintelTemplate.tsx
@@ -1,8 +1,8 @@
import React, { Suspense } from 'react';
import { graphql, PreloadedQuery, usePreloadedQuery } from 'react-relay';
import { useParams } from 'react-router-dom';
-import FintelTemplatePreview from '@components/settings/sub_types/fintel_templates/FintelTemplatePreview';
-import { FintelTemplateProvider } from '@components/settings/sub_types/fintel_templates/FintelTemplateContext';
+import FintelTemplatePreview from './FintelTemplatePreview';
+import { FintelTemplateProvider } from './FintelTemplateContext';
import FintelTemplateContentEditor from './FintelTemplateContentEditor';
import FintelTemplateTabs from './FintelTemplateTabs';
import FintelTemplateHeader from './FintelTemplateHeader';
@@ -25,6 +25,7 @@ export const fintelTemplateQuery = graphql`
...FintelTemplateHeader_template
...FintelTemplateContentEditor_template
...FintelTemplateWidgetsSidebar_template
+ ...FintelTemplatePreview_template
}
}
`;
@@ -55,7 +56,7 @@ const FintelTemplateComponent = ({ queryRef }: FintelTemplateProps) => {
diff --git a/opencti-platform/opencti-front/src/private/components/settings/sub_types/fintel_templates/FintelTemplatePreview.tsx b/opencti-platform/opencti-front/src/private/components/settings/sub_types/fintel_templates/FintelTemplatePreview.tsx
index 9876a3a8cf94c..4551ba50327ad 100644
--- a/opencti-platform/opencti-front/src/private/components/settings/sub_types/fintel_templates/FintelTemplatePreview.tsx
+++ b/opencti-platform/opencti-front/src/private/components/settings/sub_types/fintel_templates/FintelTemplatePreview.tsx
@@ -2,25 +2,52 @@ import React, { CSSProperties, useEffect, useState } from 'react';
import { Paper } from '@mui/material';
import { useTheme } from '@mui/styles';
import Typography from '@mui/material/Typography';
-import { useFragment } from 'react-relay';
-import { FintelTemplateQuery$data } from './__generated__/FintelTemplateQuery.graphql';
-import { FintelTemplateWidgetsSidebar_template$key } from './__generated__/FintelTemplateWidgetsSidebar_template.graphql';
+import { graphql, useFragment } from 'react-relay';
import { useFintelTemplateContext } from './FintelTemplateContext';
-import { widgetsFragment } from './FintelTemplateWidgetsSidebar';
import type { Theme } from '../../../../../components/Theme';
import { useFormatter } from '../../../../../components/i18n';
import FintelTemplatePreviewForm, { FintelTemplatePreviewFormInputs } from './FintelTemplatePreviewForm';
import useFileFromTemplate from '../../../../../utils/outcome_template/engine/useFileFromTemplate';
import { htmlToPdfReport } from '../../../../../utils/htmlToPdf/htmlToPdf';
import PdfViewer from '../../../../../components/PdfViewer';
+import { FintelTemplatePreview_template$key } from './__generated__/FintelTemplatePreview_template.graphql';
+import { EngineFintelTemplateQuery$data } from '../../../../../utils/outcome_template/engine/__generated__/EngineFintelTemplateQuery.graphql';
+
+const previewFragment = graphql`
+ fragment FintelTemplatePreview_template on FintelTemplate {
+ fintel_template_widgets {
+ variable_name
+ widget {
+ id
+ type
+ dataSelection {
+ instance_id
+ filters
+ dynamicFrom
+ dynamicTo
+ date_attribute
+ number
+ attribute
+ isTo
+ columns {
+ label
+ variableName
+ attribute
+ displayStyle
+ }
+ }
+ }
+ }
+ }
+`;
interface FintelTemplatePreviewProps {
- fintelTemplate: NonNullable;
+ data: FintelTemplatePreview_template$key;
isTabActive: boolean
}
const FintelTemplatePreview = ({
- fintelTemplate,
+ data,
isTabActive,
}: FintelTemplatePreviewProps) => {
const theme = useTheme();
@@ -31,9 +58,9 @@ const FintelTemplatePreview = ({
const [pdf, setPdf] = useState();
const [formValues, setFormValues] = useState();
- const { fintel_template_widgets } = useFragment(
- widgetsFragment,
- fintelTemplate,
+ const { fintel_template_widgets } = useFragment(
+ previewFragment,
+ data,
);
const paperStyle: CSSProperties = {
@@ -48,13 +75,14 @@ const FintelTemplatePreview = ({
maxMarkings: string[],
fileMarkings: string[],
) => {
- const htmlTemplate = await buildFileFromTemplate(scoId, maxMarkings, undefined, {
+ const template = {
template_content: editorValue ?? '',
name: 'Preview',
id: 'preview',
fintel_template_widgets,
instance_filters: null,
- });
+ } as unknown as EngineFintelTemplateQuery$data['fintelTemplate'];
+ const htmlTemplate = await buildFileFromTemplate(scoId, maxMarkings, undefined, template);
const PDF = await htmlToPdfReport(scoName, htmlTemplate, 'Preview', fileMarkings);
PDF.getBlob((blob) => {
const file = new File([blob], 'Preview.pdf', { type: blob.type });
diff --git a/opencti-platform/opencti-front/src/private/components/settings/sub_types/fintel_templates/FintelTemplateWidgetsList.tsx b/opencti-platform/opencti-front/src/private/components/settings/sub_types/fintel_templates/FintelTemplateWidgetsList.tsx
index 7a91d5fb5af9e..60a8210233f1c 100644
--- a/opencti-platform/opencti-front/src/private/components/settings/sub_types/fintel_templates/FintelTemplateWidgetsList.tsx
+++ b/opencti-platform/opencti-front/src/private/components/settings/sub_types/fintel_templates/FintelTemplateWidgetsList.tsx
@@ -1,15 +1,14 @@
-import List from '@mui/material/List';
-import IconButton from '@mui/material/IconButton';
+import React, { FunctionComponent, MouseEvent, useMemo, useState } from 'react';
import { AddOutlined } from '@mui/icons-material';
-import React, { FunctionComponent, MouseEvent } from 'react';
-import { Tooltip } from '@mui/material';
-import Typography from '@mui/material/Typography';
+import { Menu, MenuItem, Tooltip, IconButton, List, Typography } from '@mui/material';
import { useTheme } from '@mui/styles';
import FintelTemplateWidgetDefault from './FintelTemplateWidgetDefault';
import FintelTemplateWidgetAttribute from './FintelTemplateWidgetAttribute';
import { useFormatter } from '../../../../../components/i18n';
import type { Theme } from '../../../../../components/Theme';
import type { Widget } from '../../../../../utils/widget/widget';
+import { MESSAGING$ } from '../../../../../relay/environment';
+import { useFintelTemplateContext } from './FintelTemplateContext';
export interface FintelTemplateWidget {
variable_name: string
@@ -18,19 +17,61 @@ export interface FintelTemplateWidget {
interface FintelTemplateWidgetsListProps {
widgets: FintelTemplateWidget[]
- handleOpenPopover: (event: MouseEvent, lineKey: string) => void
- title: string
- onCreateWidget?: () => void
+ onCreateWidget: () => void
+ onDeleteWidget: (w: FintelTemplateWidget) => void
+ onUpdateWidget: (w: FintelTemplateWidget) => void
}
const FintelTemplateWidgetsList: FunctionComponent = ({
widgets,
- handleOpenPopover,
- title,
onCreateWidget,
+ onDeleteWidget,
+ onUpdateWidget,
}) => {
- const { t_i18n } = useFormatter();
const theme = useTheme();
+ const { t_i18n } = useFormatter();
+ const { editorValue } = useFintelTemplateContext();
+
+ const [menuAnchor, setMenuAnchor] = useState(null);
+ const [selectedWidget, setSelectedWidget] = useState();
+
+ const isSelectedWidgetUsed = useMemo(() => {
+ if (!selectedWidget) return false;
+ return !!editorValue?.includes(`$${selectedWidget.variable_name}`);
+ }, [selectedWidget, editorValue]);
+
+ const openPopover = (e: MouseEvent, varName: string) => {
+ const widget = widgets.find((w) => w.variable_name === varName);
+ if (widget) {
+ setSelectedWidget(widget);
+ setMenuAnchor(e.currentTarget);
+ }
+ };
+
+ const copyWidgetToClipboard = async () => {
+ if (selectedWidget) {
+ await navigator.clipboard.writeText(`$${selectedWidget}`);
+ MESSAGING$.notifySuccess(t_i18n('Widget copied to clipboard'));
+ }
+ setMenuAnchor(null);
+ setSelectedWidget(undefined);
+ };
+
+ const deleteWidget = () => {
+ if (selectedWidget) {
+ onDeleteWidget(selectedWidget);
+ }
+ setMenuAnchor(null);
+ setSelectedWidget(undefined);
+ };
+
+ const updateWidget = () => {
+ if (selectedWidget) {
+ onUpdateWidget(selectedWidget);
+ }
+ setMenuAnchor(null);
+ setSelectedWidget(undefined);
+ };
return (
<>
@@ -43,19 +84,20 @@ const FintelTemplateWidgetsList: FunctionComponent
- {title}
- {onCreateWidget && (
-
-
-
-
-
- )}
+
+ {t_i18n('Available template widgets')}
+
+
+
+
+
+
+
{widgets.length === 0 && (
@@ -72,22 +114,41 @@ const FintelTemplateWidgetsList: FunctionComponent
) : (
);
})}
+
+
>
);
};
diff --git a/opencti-platform/opencti-front/src/private/components/settings/sub_types/fintel_templates/FintelTemplateWidgetsSidebar.tsx b/opencti-platform/opencti-front/src/private/components/settings/sub_types/fintel_templates/FintelTemplateWidgetsSidebar.tsx
index 0d6a517fca810..c76d0a2f2b8d0 100644
--- a/opencti-platform/opencti-front/src/private/components/settings/sub_types/fintel_templates/FintelTemplateWidgetsSidebar.tsx
+++ b/opencti-platform/opencti-front/src/private/components/settings/sub_types/fintel_templates/FintelTemplateWidgetsSidebar.tsx
@@ -1,8 +1,9 @@
-import { Drawer, SxProps, Toolbar, MenuItem, Menu } from '@mui/material';
-import React, { FunctionComponent, MouseEvent, useState } from 'react';
+import { Drawer, SxProps, Toolbar } from '@mui/material';
+import React, { FunctionComponent, useMemo, useState } from 'react';
import { useSettingsMessagesBannerHeight } from '@components/settings/settings_messages/SettingsMessagesBanner';
import { useTheme } from '@mui/styles';
import { graphql, useFragment } from 'react-relay';
+import useFintelTemplateEdit from './useFintelTemplateEdit';
import { FintelTemplateWidgetsSidebar_template$key } from './__generated__/FintelTemplateWidgetsSidebar_template.graphql';
import FintelTemplateWidgetsList, { FintelTemplateWidget } from './FintelTemplateWidgetsList';
import { useFormatter } from '../../../../../components/i18n';
@@ -10,17 +11,15 @@ import type { Theme } from '../../../../../components/Theme';
import { MESSAGING$ } from '../../../../../relay/environment';
import WidgetConfig from '../../../widgets/WidgetConfig';
import type { Widget } from '../../../../../utils/widget/widget';
-import useApiMutation from '../../../../../utils/hooks/useApiMutation';
import { emptyFilterGroup, removeIdFromFilterGroupObject } from '../../../../../utils/filters/filtersUtils';
import DeleteDialog from '../../../../../components/DeleteDialog';
import useDeletion from '../../../../../utils/hooks/useDeletion';
export const FINTEL_TEMPLATE_SIDEBAR_WIDTH = 350;
-export const widgetsFragment = graphql`
+const sidebarFragment = graphql`
fragment FintelTemplateWidgetsSidebar_template on FintelTemplate {
id
- template_content
fintel_template_widgets {
variable_name
widget {
@@ -56,31 +55,11 @@ export const widgetsFragment = graphql`
legend
distributed
}
- layout {
- w
- h
- x
- y
- i
- moved
- static
- }
}
}
}
`;
-export const fintelTemplateWidgetMutationFieldPatch = graphql`
- mutation FintelTemplateWidgetsSidebarWidgetsFieldPatchMutation(
- $id: ID!
- $input: [EditInput!]!
- ) {
- fintelTemplateFieldPatch(id: $id, input: $input) {
- ...FintelTemplateWidgetsSidebar_template
- }
- }
-`;
-
interface FintelTemplateWidetsSidebarProps {
data: FintelTemplateWidgetsSidebar_template$key,
}
@@ -89,9 +68,21 @@ const FintelTemplateWidgetsSidebar: FunctionComponent();
const { t_i18n } = useFormatter();
const settingsMessagesBannerHeight = useSettingsMessagesBannerHeight();
- const { id, template_content, fintel_template_widgets: fintelTemplateWidgets } = useFragment(widgetsFragment, data);
- const formattedFintelTemplateWidgets: FintelTemplateWidget[] = fintelTemplateWidgets
+ const { id, fintel_template_widgets } = useFragment(sidebarFragment, data);
+
+ const [commitEditMutation] = useFintelTemplateEdit();
+ const deletion = useDeletion({});
+ const { handleCloseDelete, handleOpenDelete } = deletion;
+
+ const [isWidgetFormOpen, setIsWidgetFormOpen] = useState(false);
+ const [selectedWidget, setSelectedWidget] = useState();
+
+ const selectedWidgetIndex = useMemo(() => {
+ return fintel_template_widgets.findIndex((w) => w.variable_name === selectedWidget?.variable_name);
+ }, [fintel_template_widgets, selectedWidget]);
+
+ const formattedFintelTemplateWidgets: FintelTemplateWidget[] = fintel_template_widgets
.map((template) => ({
...template,
widget: {
@@ -105,86 +96,44 @@ const FintelTemplateWidgetsSidebar: FunctionComponent (template.widget.type === 'attribute'
- ? (template.widget.dataSelection[0].columns ?? []).map((c) => ({
- id: template.widget.id,
- variableName: c.variableName,
- type: template.widget.type,
- }))
- : {
- id: template.widget.id,
- variableName: template.variable_name,
- type: template.widget.type,
- }))
- .flat() as { id: string, variableName: string, type: string }[];
-
- const usedWidgets = formattedFintelTemplateWidgetsForList.filter((w) => template_content.includes(`$${w.variableName}`));
-
- const [selectedVariable, setSelectedVariable] = useState(undefined);
- const [isWidgetFormOpen, setIsWidgetFormOpen] = useState(false);
- const [anchorEl, setAnchorEl] = useState(null);
-
- const [commitWidgetUpdate] = useApiMutation(fintelTemplateWidgetMutationFieldPatch);
- const deletion = useDeletion({});
- const { handleCloseDelete, handleOpenDelete } = deletion;
-
- const selectedWidget = formattedFintelTemplateWidgets.find((t) => t.variable_name === selectedVariable)?.widget as Widget ?? undefined;
- const selectedWidgetIndex = formattedFintelTemplateWidgets.map((t) => t.variable_name).indexOf(selectedVariable ?? '');
-
- const paperStyle: SxProps = {
- '.MuiDrawer-paper': {
- width: FINTEL_TEMPLATE_SIDEBAR_WIDTH,
- padding: `${theme.spacing(2)} 0`,
- paddingTop: `calc(${theme.spacing(2)} + ${settingsMessagesBannerHeight}px)`,
- },
- };
-
- const handleOpenPopover = (event: MouseEvent, variable: string) => {
- setAnchorEl(event.currentTarget);
- setSelectedVariable(variable);
+ const onOpenUpdate = (widget: FintelTemplateWidget) => {
+ setSelectedWidget(widget);
+ setIsWidgetFormOpen(true);
};
- const handleOpenUpdate = () => {
- setIsWidgetFormOpen(true);
+ const onOpenDelete = (widget: FintelTemplateWidget) => {
+ setSelectedWidget(widget);
+ handleOpenDelete();
};
const handleWidgetConfigOpen = (isOpen: boolean) => {
setIsWidgetFormOpen(isOpen);
if (!isOpen) {
- setAnchorEl(null);
- setSelectedVariable(undefined);
+ setSelectedWidget(undefined);
}
};
- const handleClosePopover = () => {
- handleWidgetConfigOpen(false);
+ const closeDeleteConfirm = () => {
+ handleCloseDelete();
+ setSelectedWidget(undefined);
};
- const onOpenDelete = () => {
- handleOpenDelete();
- setAnchorEl(null);
- };
-
- const handleDeleteWidget = () => {
+ const submitDeleteWidget = () => {
if (selectedWidgetIndex < 0) {
throw Error('Selected widget index should be positive.');
}
- const editInput = {
- key: 'fintel_template_widgets',
- object_path: `fintel_template_widgets/${selectedWidgetIndex}`,
- value: [null],
- operation: 'remove',
- };
- commitWidgetUpdate({
+ commitEditMutation({
variables: {
id,
- input: editInput,
- },
- onCompleted: () => {
- handleCloseDelete();
- setSelectedVariable(undefined);
+ input: [{
+ key: 'fintel_template_widgets',
+ object_path: `fintel_template_widgets/${selectedWidgetIndex}`,
+ value: [null],
+ operation: 'remove',
+ }],
},
+ onError: closeDeleteConfirm,
+ onCompleted: closeDeleteConfirm,
});
};
@@ -208,44 +157,41 @@ const FintelTemplateWidgetsSidebar: FunctionComponent {
- setIsWidgetFormOpen(true);
- };
-
- const copyWidgetToClipboard = async () => {
- if (selectedVariable) {
- await navigator.clipboard.writeText(`$${selectedVariable}`);
- MESSAGING$.notifySuccess(t_i18n('Widget copied to clipboard'));
- }
- setAnchorEl(null);
+ const paperStyle: SxProps = {
+ '.MuiDrawer-paper': {
+ width: FINTEL_TEMPLATE_SIDEBAR_WIDTH,
+ padding: `${theme.spacing(2)} 0`,
+ paddingTop: `calc(${theme.spacing(2)} + ${settingsMessagesBannerHeight}px)`,
+ },
};
return (
@@ -255,46 +201,27 @@ const FintelTemplateWidgetsSidebar: FunctionComponent
setIsWidgetFormOpen(true)}
widgets={formattedFintelTemplateWidgets}
- handleOpenPopover={handleOpenPopover}
+ onUpdateWidget={onOpenUpdate}
+ onDeleteWidget={onOpenDelete}
/>
-
-
>
);