Skip to content
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
60 changes: 30 additions & 30 deletions web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,11 @@
"@patternfly/react-icons": "^6.2.0",
"@patternfly/react-table": "^6.2.0",
"@patternfly/react-templates": "^6.2.0",
"@perses-dev/components": "0.53.0-beta.4",
"@perses-dev/core": "0.53.0-beta.3",
"@perses-dev/dashboards": "0.53.0-beta.4",
"@perses-dev/explore": "0.53.0-beta.4",
"@perses-dev/plugin-system": "0.53.0-beta.4",
"@perses-dev/components": "0.53.0-rc.1",
"@perses-dev/core": "0.53.0-beta.4",
"@perses-dev/dashboards": "0.53.0-rc.1",
"@perses-dev/explore": "0.53.0-rc.1",
"@perses-dev/plugin-system": "0.53.0-rc.1",
"@prometheus-io/codemirror-promql": "^0.37.0",
"@tanstack/react-query": "^4.36.1",
"@types/ajv": "^0.0.5",
Expand Down Expand Up @@ -184,4 +184,4 @@
"@console/pluginAPI": "*"
}
}
}
}
2 changes: 0 additions & 2 deletions web/src/components/dashboards/perses/PersesWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,6 @@ export function PersesWrapper({ children, project }: PersesWrapperProps) {
<SnackbarProvider
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
variant="default"
content=""
>
<PluginRegistry pluginLoader={pluginLoader}>
{!project ? (
Expand Down Expand Up @@ -461,7 +460,6 @@ function InnerWrapper({ children, project, dashboardName }) {
{clearedDashboardResource ? (
<DashboardProvider
initialState={{
isEditMode: false,
dashboardResource: clearedDashboardResource,
}}
>
Expand Down
65 changes: 65 additions & 0 deletions web/src/components/dashboards/perses/ToastProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React, { createContext, useContext, useState, ReactNode } from 'react';
import {
Alert,
AlertProps,
AlertGroup,
AlertActionCloseButton,
AlertVariant,
} from '@patternfly/react-core';

interface ToastItem {
key: string;
title: string;
variant: AlertProps['variant'];
}

interface ToastContextType {
addAlert: (title: string, variant: AlertProps['variant']) => void;
removeAlert: (key: string) => void;
alerts: ToastItem[];
}

const ToastContext = createContext<ToastContextType | undefined>(undefined);

export const useToast = () => {
const context = useContext(ToastContext);
if (!context) {
throw new Error('useAlerts must be used within AlertProvider');
}
return context;
};

export const ToastProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
const [alerts, setAlerts] = useState<ToastItem[]>([]);

const addAlert = (title: string, variant: AlertProps['variant']) => {
const key = new Date().getTime().toString();
setAlerts((prevAlerts) => [{ title, variant, key }, ...prevAlerts]);
};

const removeAlert = (key: string) => {
setAlerts((prevAlerts) => prevAlerts.filter((alert) => alert.key !== key));
};

return (
<ToastContext.Provider value={{ addAlert, removeAlert, alerts }}>
{children}
<AlertGroup hasAnimations isToast isLiveRegion>
{alerts.map(({ key, variant, title }) => (
<Alert
variant={AlertVariant[variant]}
title={title}
actionClose={
<AlertActionCloseButton
title={title}
variantLabel={`${variant} alert`}
onClose={() => removeAlert(key)}
/>
}
key={key}
/>
))}
</AlertGroup>
</ToastContext.Provider>
);
};
60 changes: 55 additions & 5 deletions web/src/components/dashboards/perses/dashboard-app.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { ReactElement, ReactNode, useState } from 'react';
import { ReactElement, ReactNode, useState, useCallback, useEffect } from 'react';
import { Box } from '@mui/material';
import { ChartsProvider, ErrorAlert, ErrorBoundary, useChartsTheme } from '@perses-dev/components';
import { DashboardResource, EphemeralDashboardResource } from '@perses-dev/core';
import {
DashboardResource,
EphemeralDashboardResource,
getResourceExtendedDisplayName,
} from '@perses-dev/core';
import { useDatasourceStore } from '@perses-dev/plugin-system';
import {
PanelDrawer,
Expand All @@ -16,12 +20,14 @@ import {
LeaveDialog,
} from '@perses-dev/dashboards';
import {
OnSaveDashboard,
useDashboard,
useDiscardChangesConfirmationDialog,
useEditMode,
} from '@perses-dev/dashboards';
import { OCPDashboardToolbar } from './dashboard-toolbar';
import { useUpdateDashboardMutation } from './dashboard-api';
import { useTranslation } from 'react-i18next';
import { useToast } from './ToastProvider';

export interface DashboardAppProps {
dashboardResource: DashboardResource | EphemeralDashboardResource;
Expand All @@ -35,7 +41,6 @@ export interface DashboardAppProps {
// when navigating away with unsaved changes (closing tab, ...).
isLeavingConfirmDialogEnabled?: boolean;
dashboardTitleComponent?: ReactNode;
onSave?: OnSaveDashboard;
onDiscard?: (entity: DashboardResource) => void;
}

Expand All @@ -49,17 +54,28 @@ export const OCPDashboardApp = (props: DashboardAppProps): ReactElement => {
isCreating,
isInitialVariableSticky,
isLeavingConfirmDialogEnabled,
onSave,
onDiscard,
} = props;

const { t } = useTranslation(process.env.I18N_NAMESPACE);

const chartsTheme = useChartsTheme();
const { addAlert } = useToast();

const { isEditMode, setEditMode } = useEditMode();
const { dashboard, setDashboard } = useDashboard();

const [originalDashboard, setOriginalDashboard] = useState<
DashboardResource | EphemeralDashboardResource | undefined
>(undefined);
const [saveErrorOccurred, setSaveErrorOccurred] = useState(false);

useEffect(() => {
if (saveErrorOccurred && !isEditMode) {
setEditMode(true);
setSaveErrorOccurred(false);
}
}, [isEditMode, saveErrorOccurred, setEditMode]);
const { setSavedDatasources } = useDatasourceStore();

const { openDiscardChangesConfirmationDialog, closeDiscardChangesConfirmationDialog } =
Expand Down Expand Up @@ -99,6 +115,40 @@ export const OCPDashboardApp = (props: DashboardAppProps): ReactElement => {
}
};

const updateDashboardMutation = useUpdateDashboardMutation();

const onSave = useCallback(
async (data: DashboardResource | EphemeralDashboardResource) => {
if (data.kind !== 'Dashboard') {
throw new Error('Invalid kind');
}

try {
const result = await updateDashboardMutation.mutateAsync(data, {
onSuccess: (updatedDashboard: DashboardResource) => {
addAlert(
t(
`Dashboard ${getResourceExtendedDisplayName(
updatedDashboard,
)} has been successfully updated`,
),
'success',
);

setSaveErrorOccurred(false);
return updatedDashboard;
},
});
return result;
} catch (error) {
addAlert(`${error}`, 'danger');
setSaveErrorOccurred(true);
return null;
}
},
[updateDashboardMutation, addAlert, t],
);

return (
<Box
sx={{
Expand Down
31 changes: 17 additions & 14 deletions web/src/components/dashboards/perses/dashboard-frame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { CombinedDashboardMetadata } from './hooks/useDashboardsData';
import { ProjectBar } from './project/ProjectBar';
import { PersesWrapper } from './PersesWrapper';
import { Overview } from '@openshift-console/dynamic-plugin-sdk';
import { ToastProvider } from './ToastProvider';

interface DashboardFrameProps {
activeProject: string | null;
Expand All @@ -26,20 +27,22 @@ export const DashboardFrame: React.FC<DashboardFrameProps> = ({
return (
<>
<ProjectBar activeProject={activeProject} setActiveProject={setActiveProject} />
<PersesWrapper project={activeProject}>
{activeProjectDashboardsMetadata?.length === 0 ? (
<DashboardEmptyState />
) : (
<DashboardHeader
boardItems={activeProjectDashboardsMetadata}
changeBoard={changeBoard}
dashboardName={dashboardName}
activeProject={activeProject}
>
<Overview>{children}</Overview>
</DashboardHeader>
)}
</PersesWrapper>
<ToastProvider>
<PersesWrapper project={activeProject}>
{activeProjectDashboardsMetadata?.length === 0 ? (
<DashboardEmptyState />
) : (
<DashboardHeader
boardItems={activeProjectDashboardsMetadata}
changeBoard={changeBoard}
dashboardName={dashboardName}
activeProject={activeProject}
>
<Overview>{children}</Overview>
</DashboardHeader>
)}
</PersesWrapper>
</ToastProvider>
</>
);
};
Loading