Skip to content

[frontend] Saved Filters Edition (#10383) #10808

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
May 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions opencti-platform/opencti-front/lang/front/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -2731,6 +2731,7 @@
"Save template": "Vorlage speichern",
"Saved filter successfully created": "Gespeicherter Filter erfolgreich erstellt",
"Saved filter successfully removed": "Gespeicherter Filter erfolgreich entfernt",
"Saved filter successfully updated": "Gespeicherter Filter erfolgreich aktualisiert",
"Scale": "Maßstab",
"Scale configuration": "Konfiguration skalieren",
"Scenario generation in progress...": "Szenarioerstellung in Arbeit...",
Expand Down Expand Up @@ -3365,6 +3366,7 @@
"Update dashboard": "Dashboard aktualisieren",
"Update date": "Aktualisierungsdatum",
"Update entities": "Entitäten aktualisieren",
"Update filter": "Filter aktualisieren",
"Update instance triggers": "Update-Instanz-Trigger",
"Update investigation": "Untersuchung aktualisieren",
"Update opinion": "Meinung aktualisieren",
Expand Down
2 changes: 2 additions & 0 deletions opencti-platform/opencti-front/lang/front/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2731,6 +2731,7 @@
"Save template": "Save template",
"Saved filter successfully created": "Saved filter successfully created",
"Saved filter successfully removed": "Saved filter successfully removed",
"Saved filter successfully updated": "Saved filter successfully updated",
"Scale": "Scale",
"Scale configuration": "Scale configuration",
"Scenario generation in progress...": "Scenario generation in progress...",
Expand Down Expand Up @@ -3365,6 +3366,7 @@
"Update dashboard": "Update dashboard",
"Update date": "Update date",
"Update entities": "Update entities",
"Update filter": "Update filter",
"Update instance triggers": "Update instance triggers",
"Update investigation": "Update investigation",
"Update opinion": "Update opinion",
Expand Down
2 changes: 2 additions & 0 deletions opencti-platform/opencti-front/lang/front/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -2731,6 +2731,7 @@
"Save template": "Guardar plantilla",
"Saved filter successfully created": "Filtro guardado creado con éxito",
"Saved filter successfully removed": "Filtro guardado eliminado correctamente",
"Saved filter successfully updated": "Filtro guardado actualizado correctamente",
"Scale": "Intervalo",
"Scale configuration": "Configuración de escala",
"Scenario generation in progress...": "Generación de escenario en curso...",
Expand Down Expand Up @@ -3365,6 +3366,7 @@
"Update dashboard": "Actualizar el cuadro de mandos",
"Update date": "Fecha de actualización",
"Update entities": "Actualizar entidades",
"Update filter": "Actualizar filtro",
"Update instance triggers": "Desencadenadores de instancias de actualización",
"Update investigation": "Actualizar investigación",
"Update opinion": "Actualizar la opinión",
Expand Down
2 changes: 2 additions & 0 deletions opencti-platform/opencti-front/lang/front/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -2731,6 +2731,7 @@
"Save template": "Enregistrer le modèle",
"Saved filter successfully created": "Filtre sauvegardé créé avec succès",
"Saved filter successfully removed": "Filtre sauvegardé supprimé avec succès",
"Saved filter successfully updated": "Filtre sauvegardé mis à jour avec succès",
"Scale": "Intervalle",
"Scale configuration": "Configuration de l'intervalle",
"Scenario generation in progress...": "Génération de scénario en cours...",
Expand Down Expand Up @@ -3365,6 +3366,7 @@
"Update dashboard": "Modifier le tableau de bord",
"Update date": "Date de mise à jour",
"Update entities": "Modifier des entités",
"Update filter": "Modifier le filtre",
"Update instance triggers": "Mettre à jour les déclencheurs de l’instance",
"Update investigation": "Modifier l'investigation",
"Update opinion": "Modifier l'opinion",
Expand Down
2 changes: 2 additions & 0 deletions opencti-platform/opencti-front/lang/front/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -2731,6 +2731,7 @@
"Save template": "テンプレートの保存",
"Saved filter successfully created": "フィルタの保存に成功",
"Saved filter successfully removed": "保存されたフィルターは正常に削除されました",
"Saved filter successfully updated": "保存されたフィルターは正常に更新されました",
"Scale": "間隔",
"Scale configuration": "スケール構成",
"Scenario generation in progress...": "シナリオ生成中...",
Expand Down Expand Up @@ -3365,6 +3366,7 @@
"Update dashboard": "ダッシュボードの更新",
"Update date": "更新日",
"Update entities": "エンティティの更新",
"Update filter": "フィルターの更新",
"Update instance triggers": "インスタンストリガーの更新",
"Update investigation": "アップデート調査",
"Update opinion": "オピニオンの更新",
Expand Down
2 changes: 2 additions & 0 deletions opencti-platform/opencti-front/lang/front/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -2731,6 +2731,7 @@
"Save template": "템플릿 저장",
"Saved filter successfully created": "저장된 필터가 성공적으로 생성되었습니다",
"Saved filter successfully removed": "저장된 필터가 성공적으로 제거되었습니다",
"Saved filter successfully updated": "저장된 필터가 성공적으로 업데이트되었습니다",
"Scale": "규모",
"Scale configuration": "규모 구성",
"Scenario generation in progress...": "시나리오 생성 진행 중...",
Expand Down Expand Up @@ -3365,6 +3366,7 @@
"Update dashboard": "대시보드 업데이트",
"Update date": "날짜 업데이트",
"Update entities": "엔터티 업데이트",
"Update filter": "필터 업데이트",
"Update instance triggers": "인스턴스 트리거 업데이트",
"Update investigation": "조사 업데이트",
"Update opinion": "의견 업데이트",
Expand Down
2 changes: 2 additions & 0 deletions opencti-platform/opencti-front/lang/front/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -2731,6 +2731,7 @@
"Save template": "保存模板",
"Saved filter successfully created": "保存的过滤器已成功创建",
"Saved filter successfully removed": "已保存过滤器成功删除",
"Saved filter successfully updated": "保存的过滤器更新成功",
"Scale": "间隔",
"Scale configuration": "比例配置",
"Scenario generation in progress...": "场景生成中...",
Expand Down Expand Up @@ -3365,6 +3366,7 @@
"Update dashboard": "更新仪表板",
"Update date": "更新日期",
"Update entities": "更新实体",
"Update filter": "更新过滤器",
"Update instance triggers": "更新实例触发器",
"Update investigation": "更新调查",
"Update opinion": "更新意见",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ type DeleteDialogProps = {
warning?: {
title?: string,
message: string,
}
},
isOpen?: boolean;
};

const DeleteDialog: React.FC<DeleteDialogProps> = ({
Expand All @@ -28,11 +29,12 @@ const DeleteDialog: React.FC<DeleteDialogProps> = ({
onClose,
message,
warning,
isOpen,
}) => {
const { t_i18n } = useFormatter();
return (
<Dialog
open={deletion.displayDelete}
open={isOpen ?? deletion.displayDelete}
slotProps={{ paper: { elevation: 1 } }}
keepMounted={true}
slots={{ transition: Transition }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,44 +5,111 @@ import { useFormatter } from 'src/components/i18n';
import Tooltip from '@mui/material/Tooltip';
import SavedFilterCreateDialog from 'src/components/saved_filters/SavedFilterCreateDialog';
import { useDataTableContext } from 'src/components/dataGrid/components/DataTableContext';
import { graphql } from 'react-relay';
import useApiMutation from 'src/utils/hooks/useApiMutation';
import { type SavedFiltersSelectionData } from 'src/components/saved_filters/SavedFilterSelection';
import Badge from '@mui/material/Badge';

const SavedFilterButton = () => {
const savedFilterButtonEditMutation = graphql`
mutation SavedFilterButtonEditMutation($id: ID!, $filters: String!) {
savedFilterFieldPatch(id: $id, filters: $filters) {
id
name
filters
scope
}
}
`;

type SavedFilterButtonProps = {
currentSavedFilter?: SavedFiltersSelectionData
setCurrentSavedFilter: (savedFilter: SavedFiltersSelectionData | undefined) => void;
};

const SavedFilterButton = ({ currentSavedFilter, setCurrentSavedFilter }: SavedFilterButtonProps) => {
const { t_i18n } = useFormatter();

const [isSavedDialogOpen, setIsSavedDialogOpen] = useState<boolean>(false);

const {
useDataTablePaginationLocalStorage: {
helpers,
viewStorage: { filters },
localStorageKey,
},
} = useDataTableContext();

const isEmptyFilters = !filters?.filters.length && !filters?.filterGroups.length;
const hasSameFilters = currentSavedFilter?.filters === JSON.stringify(filters);

const [commit] = useApiMutation(
savedFilterButtonEditMutation,
undefined,
{ successMessage: 'Saved filter successfully updated' },
);

const handleEditSavedFilter = () => {
if (!currentSavedFilter) return;
commit({
variables: {
id: currentSavedFilter.id,
filters: JSON.stringify(filters),
},
onCompleted: () => {
const newValue = {
...currentSavedFilter,
filters: JSON.stringify(filters),
};
setCurrentSavedFilter(newValue);
helpers.handleChangeSavedFilters(newValue);
},
});
};

const handleOpenDialog = () => setIsSavedDialogOpen(true);
const handleCloseDialog = () => setIsSavedDialogOpen(false);

const handleSaveButtonClick = () => {
if (!hasSameFilters && currentSavedFilter) {
handleEditSavedFilter();
} else {
handleOpenDialog();
}
};

const renderBadge = () => (
<Badge color="warning" overlap="circular" variant="dot">
<SaveOutlined />
</Badge>
);
const isDisabled = isEmptyFilters || hasSameFilters;

const isRestrictedStorageKey = localStorageKey.includes('_stixCoreRelationshipCreationFromEntity');
if (isRestrictedStorageKey) return null;

return (
<>
<Tooltip title={t_i18n('Save filter')}>
<Tooltip title={!isDisabled && currentSavedFilter ? t_i18n('Update filter') : t_i18n('Save filter')}>
<span>
<IconButton
color="primary"
onClick={handleOpenDialog}
onClick={handleSaveButtonClick}
size="small"
disabled={!filters?.filters.length && !filters?.filterGroups.length}
disabled={isDisabled}
aria-label={t_i18n('Save')}
>
<SaveOutlined />
{!hasSameFilters && currentSavedFilter
? renderBadge()
: <SaveOutlined />
}
</IconButton>
</span>
</Tooltip>

{isSavedDialogOpen && (
<SavedFilterCreateDialog
isOpen={isSavedDialogOpen}
setCurrentSavedFilter={setCurrentSavedFilter}
onClose={handleCloseDialog}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import TextField from '@mui/material/TextField';
import { graphql } from 'react-relay';
import { useDataTableContext } from 'src/components/dataGrid/components/DataTableContext';
import { insertNode } from 'src/utils/store';
import { type SavedFiltersSelectionData } from 'src/components/saved_filters/SavedFilterSelection';
import { SavedFilterCreateDialogMutation$data } from 'src/components/saved_filters/__generated__/SavedFilterCreateDialogMutation.graphql';
import useApiMutation from '../../utils/hooks/useApiMutation';
import getSavedFilterScopeFilter from './getSavedFilterScopeFilter';

Expand All @@ -26,14 +28,16 @@ const savedFilterCreateDialogMutation = graphql`
type SavedFilterDialogProps = {
onClose: () => void;
isOpen: boolean;
setCurrentSavedFilter: (savedFilter: SavedFiltersSelectionData | undefined) => void;
};

const SavedFilterCreateDialog = ({ isOpen, onClose }: SavedFilterDialogProps) => {
const SavedFilterCreateDialog = ({ isOpen, onClose, setCurrentSavedFilter }: SavedFilterDialogProps) => {
const { t_i18n } = useFormatter();

const {
useDataTablePaginationLocalStorage: {
localStorageKey,
helpers,
viewStorage: { filters },
},
} = useDataTableContext();
Expand Down Expand Up @@ -65,7 +69,11 @@ const SavedFilterCreateDialog = ({ isOpen, onClose }: SavedFilterDialogProps) =>
const scopeFilter = getSavedFilterScopeFilter(localStorageKey);
insertNode(store, 'SavedFilters_savedFilters', { filters: scopeFilter }, 'savedFilterAdd');
},
onCompleted: () => {
onCompleted: (response) => {
const { savedFilterAdd } = response as SavedFilterCreateDialogMutation$data;
if (!savedFilterAdd) return;
setCurrentSavedFilter(savedFilterAdd);
helpers.handleChangeSavedFilters(savedFilterAdd);
onClose();
},
onError: () => {
Expand All @@ -77,7 +85,7 @@ const SavedFilterCreateDialog = ({ isOpen, onClose }: SavedFilterDialogProps) =>
return (
<Dialog
open={isOpen}
PaperProps={{ elevation: 1 }}
slotProps={{ paper: { elevation: 1 } }}
onClose={onClose}
fullWidth
maxWidth="xs"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import React from 'react';
import DialogContent from '@mui/material/DialogContent';
import DialogActions from '@mui/material/DialogActions';
import Button from '@mui/material/Button';
import Dialog from '@mui/material/Dialog';
import { graphql } from 'react-relay';
import { useFormatter } from 'src/components/i18n';
import { deleteNode } from 'src/utils/store';
import DialogContentText from '@mui/material/DialogContentText';
import getSavedFilterScopeFilter from 'src/components/saved_filters/getSavedFilterScopeFilter';
import { useDataTableContext } from 'src/components/dataGrid/components/DataTableContext';
import DeleteDialog from 'src/components/DeleteDialog';
import useDeletion from 'src/utils/hooks/useDeletion';
import useApiMutation from '../../utils/hooks/useApiMutation';

const savedFilterDeleteDialogMutation = graphql`
Expand Down Expand Up @@ -59,24 +56,16 @@ const SavedFilterDeleteDialog = ({ savedFilterToDelete, onClose, onReset }: Save
});
};

const deletion = useDeletion({});

return (
<Dialog
open={true}
PaperProps={{ elevation: 1 }}
<DeleteDialog
deletion={deletion}
isOpen
message= {t_i18n('Do you want to delete this saved filter?')}
submitDelete={handleSubmitDeleteFilter}
onClose={onClose}
fullWidth
maxWidth="xs"
>
<DialogContent>
<DialogContentText>
{t_i18n('Do you want to delete this saved filter?')}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={onClose}>{t_i18n('Cancel')}</Button>
<Button onClick={handleSubmitDeleteFilter} color="secondary">{t_i18n('Validate')}</Button>
</DialogActions>
</Dialog>
/>
);
};

Expand Down
Loading