chore: use IRM plugin ID instead of OnCall / Incident if it's present… (#99742)

* chore: use IRM plugin ID instead of OnCall / Incident if it's present in the stack

* minor improvements

* fix unit tests

* Add IRM plugin discovery tests

---------

Co-authored-by: Konrad Lalik <konrad.lalik@grafana.com>
pull/99823/head
Dominik Broj 5 months ago committed by GitHub
parent b683724bec
commit 3589d9192d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      public/app/features/alerting/unified/api/incidentsApi.ts
  2. 27
      public/app/features/alerting/unified/api/onCallApi.test.ts
  3. 6
      public/app/features/alerting/unified/api/onCallApi.ts
  4. 20
      public/app/features/alerting/unified/components/bridges/DeclareIncidentButton.tsx
  5. 4
      public/app/features/alerting/unified/components/contact-points/useContactPoints.ts
  6. 6
      public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/onCall/useOnCallIntegration.ts
  7. 2
      public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/types.ts
  8. 12
      public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/useReceiversMetadata.ts
  9. 101
      public/app/features/alerting/unified/testSetup/plugins.ts
  10. 1
      public/app/features/alerting/unified/types/pluginBridges.ts
  11. 55
      public/app/features/alerting/unified/utils/config.test.ts
  12. 14
      public/app/features/alerting/unified/utils/config.ts
  13. 1
      public/app/features/dashboard-scene/serialization/buildNewDashboardSaveModel.test.ts
  14. 1
      public/app/features/dashboard/components/DashExportModal/DashboardExporter.test.ts
  15. 1
      public/app/features/plugins/components/AppRootPage.test.tsx
  16. 44
      public/img/alerting/irm_logo.svg

@ -1,4 +1,4 @@
import { SupportedPlugin } from '../types/pluginBridges';
import { getIrmIfPresentOrIncidentPluginId } from '../utils/config';
import { alertingApi } from './alertingApi';
@ -7,7 +7,7 @@ interface IncidentsPluginConfigDto {
isIncidentCreated: boolean;
}
const getProxyApiUrl = (path: string) => `/api/plugins/${SupportedPlugin.Incident}/resources${path}`;
const getProxyApiUrl = (path: string) => `/api/plugins/${getIrmIfPresentOrIncidentPluginId()}/resources${path}`;
export const incidentsApi = alertingApi.injectEndpoints({
endpoints: (build) => ({

@ -0,0 +1,27 @@
import { config } from '@grafana/runtime';
import { pluginMeta, pluginMetaToPluginConfig } from '../testSetup/plugins';
import { SupportedPlugin } from '../types/pluginBridges';
import { getProxyApiUrl } from './onCallApi';
describe('getProxyApiUrl', () => {
it('should return URL with IRM plugin ID when IRM plugin is present', () => {
config.apps = { [SupportedPlugin.Irm]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.Irm]) };
expect(getProxyApiUrl('/alert_receive_channels/')).toBe(
'/api/plugins/grafana-irm-app/resources/alert_receive_channels/'
);
});
it('should return URL with OnCall plugin ID when IRM plugin is not present', () => {
config.apps = {
[SupportedPlugin.OnCall]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.OnCall]),
[SupportedPlugin.Incident]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.Incident]),
};
expect(getProxyApiUrl('/alert_receive_channels/')).toBe(
'/api/plugins/grafana-oncall-app/resources/alert_receive_channels/'
);
});
});

@ -1,7 +1,7 @@
import { FetchError, isFetchError } from '@grafana/runtime';
import { GRAFANA_ONCALL_INTEGRATION_TYPE } from '../components/receivers/grafanaAppReceivers/onCall/onCall';
import { SupportedPlugin } from '../types/pluginBridges';
import { getIrmIfPresentOrOnCallPluginId } from '../utils/config';
import { alertingApi } from './alertingApi';
@ -38,7 +38,9 @@ export interface OnCallConfigChecks {
is_integration_chatops_connected: boolean;
}
const getProxyApiUrl = (path: string) => `/api/plugins/${SupportedPlugin.OnCall}/resources${path}`;
export function getProxyApiUrl(path: string) {
return `/api/plugins/${getIrmIfPresentOrOnCallPluginId()}/resources${path}`;
}
export const onCallApi = alertingApi.injectEndpoints({
endpoints: (build) => ({

@ -1,7 +1,7 @@
import { Button, LinkButton, Menu, Tooltip } from '@grafana/ui';
import { usePluginBridge } from '../../hooks/usePluginBridge';
import { SupportedPlugin } from '../../types/pluginBridges';
import { getIrmIfPresentOrIncidentPluginId } from '../../utils/config';
import { createBridgeURL } from '../PluginBridge';
interface Props {
@ -10,10 +10,16 @@ interface Props {
url?: string;
}
const pluginId = getIrmIfPresentOrIncidentPluginId();
export const DeclareIncidentButton = ({ title = '', severity = '', url = '' }: Props) => {
const bridgeURL = createBridgeURL(SupportedPlugin.Incident, '/incidents/declare', { title, severity, url });
const bridgeURL = createBridgeURL(pluginId, '/incidents/declare', {
title,
severity,
url,
});
const { loading, installed, settings } = usePluginBridge(SupportedPlugin.Incident);
const { loading, installed, settings } = usePluginBridge(pluginId);
return (
<>
@ -39,9 +45,13 @@ export const DeclareIncidentButton = ({ title = '', severity = '', url = '' }: P
};
export const DeclareIncidentMenuItem = ({ title = '', severity = '', url = '' }: Props) => {
const bridgeURL = createBridgeURL(SupportedPlugin.Incident, '/incidents/declare', { title, severity, url });
const bridgeURL = createBridgeURL(pluginId, '/incidents/declare', {
title,
severity,
url,
});
const { loading, installed, settings } = usePluginBridge(SupportedPlugin.Incident);
const { loading, installed, settings } = usePluginBridge(pluginId);
return (
<>

@ -26,7 +26,7 @@ import { useAsync } from '../../hooks/useAsync';
import { usePluginBridge } from '../../hooks/usePluginBridge';
import { useProduceNewAlertmanagerConfiguration } from '../../hooks/useProduceNewAlertmanagerConfig';
import { addReceiverAction, deleteReceiverAction, updateReceiverAction } from '../../reducers/alertmanager/receivers';
import { SupportedPlugin } from '../../types/pluginBridges';
import { getIrmIfPresentOrOnCallPluginId } from '../../utils/config';
import { enhanceContactPointsWithMetadata } from './utils';
@ -67,7 +67,7 @@ const defaultOptions = {
* Otherwise, returns no data
*/
const useOnCallIntegrations = ({ skip }: Skippable = {}) => {
const { installed, loading } = usePluginBridge(SupportedPlugin.OnCall);
const { installed, loading } = usePluginBridge(getIrmIfPresentOrOnCallPluginId());
const oncallIntegrationsResponse = useGrafanaOnCallIntegrationsQuery(undefined, { skip: skip || !installed });
return useMemo(() => {

@ -3,13 +3,13 @@ import { useCallback, useMemo } from 'react';
import { SelectableValue } from '@grafana/data';
import { isFetchError } from '@grafana/runtime';
import { getIrmIfPresentOrOnCallPluginId } from 'app/features/alerting/unified/utils/config';
import { useAppNotification } from '../../../../../../../core/copy/appNotification';
import { Receiver } from '../../../../../../../plugins/datasource/alertmanager/types';
import { NotifierDTO } from '../../../../../../../types';
import { ONCALL_INTEGRATION_V2_FEATURE, onCallApi } from '../../../../api/onCallApi';
import { usePluginBridge } from '../../../../hooks/usePluginBridge';
import { SupportedPlugin } from '../../../../types/pluginBridges';
import { option } from '../../../../utils/notifier-types';
import { GRAFANA_APP_RECEIVERS_SOURCE_IMAGE } from '../types';
@ -41,7 +41,7 @@ function useOnCallPluginStatus() {
installed: isOnCallEnabled,
loading: isPluginBridgeLoading,
error: pluginError,
} = usePluginBridge(SupportedPlugin.OnCall);
} = usePluginBridge(getIrmIfPresentOrOnCallPluginId());
const {
data: onCallFeatures = [],
@ -240,7 +240,7 @@ export function useOnCallIntegration() {
description: isOnCallEnabled
? 'Connect effortlessly to Grafana OnCall'
: 'Enable Grafana OnCall plugin to use this integration',
iconUrl: GRAFANA_APP_RECEIVERS_SOURCE_IMAGE[SupportedPlugin.OnCall],
iconUrl: GRAFANA_APP_RECEIVERS_SOURCE_IMAGE[getIrmIfPresentOrOnCallPluginId()],
},
extendOnCallNotifierFeatures,
extendOnCallReceivers,

@ -2,7 +2,7 @@ import { SupportedPlugin } from '../../../types/pluginBridges';
export const GRAFANA_APP_RECEIVERS_SOURCE_IMAGE: Record<SupportedPlugin, string> = {
[SupportedPlugin.OnCall]: 'public/img/alerting/oncall_logo.svg',
[SupportedPlugin.Irm]: 'public/img/alerting/irm_logo.svg',
[SupportedPlugin.Incident]: '',
[SupportedPlugin.MachineLearning]: '',
[SupportedPlugin.Labels]: '',

@ -1,6 +1,6 @@
import { GrafanaManagedReceiverConfig } from '../../../../../../plugins/datasource/alertmanager/types';
import { OnCallIntegrationDTO } from '../../../api/onCallApi';
import { SupportedPlugin } from '../../../types/pluginBridges';
import { getIrmIfPresentOrOnCallPluginId, getIsIrmPluginPresent } from '../../../utils/config';
import { createBridgeURL } from '../../PluginBridge';
import { GRAFANA_APP_RECEIVERS_SOURCE_IMAGE } from './types';
@ -13,7 +13,7 @@ export interface ReceiverPluginMetadata {
warning?: string;
}
const onCallReceiverICon = GRAFANA_APP_RECEIVERS_SOURCE_IMAGE[SupportedPlugin.OnCall];
const onCallReceiverICon = GRAFANA_APP_RECEIVERS_SOURCE_IMAGE[getIrmIfPresentOrOnCallPluginId()];
const onCallReceiverTitle = 'Grafana OnCall';
export const onCallReceiverMeta: ReceiverPluginMetadata = {
@ -26,6 +26,8 @@ export function getOnCallMetadata(
receiver: GrafanaManagedReceiverConfig,
hasAlertManagerConfigData = true
): ReceiverPluginMetadata {
const pluginName = getIsIrmPluginPresent() ? 'IRM' : 'OnCall';
if (!hasAlertManagerConfigData) {
return onCallReceiverMeta;
}
@ -43,7 +45,7 @@ export function getOnCallMetadata(
if (onCallIntegrations == null) {
return {
...onCallReceiverMeta,
warning: 'Grafana OnCall is not installed or is disabled',
warning: `Grafana ${pluginName} is not installed or is disabled`,
};
}
@ -55,8 +57,8 @@ export function getOnCallMetadata(
...onCallReceiverMeta,
description: matchingOnCallIntegration?.display_name,
externalUrl: matchingOnCallIntegration
? createBridgeURL(SupportedPlugin.OnCall, `/integrations/${matchingOnCallIntegration.value}`)
? createBridgeURL(getIrmIfPresentOrOnCallPluginId(), `/integrations/${matchingOnCallIntegration.value}`)
: undefined,
warning: matchingOnCallIntegration ? undefined : 'OnCall Integration no longer exists',
warning: matchingOnCallIntegration ? undefined : `${pluginName} Integration no longer exists`,
};
}

@ -1,5 +1,5 @@
import { PluginMeta, PluginType } from '@grafana/data';
import { setPluginComponentsHook, setPluginExtensionsHook } from '@grafana/runtime';
import { PluginLoadingStrategy, PluginMeta, PluginType } from '@grafana/data';
import { AppPluginConfig, setPluginComponentsHook, setPluginExtensionsHook } from '@grafana/runtime';
import { SupportedPlugin } from 'app/features/alerting/unified/types/pluginBridges';
import { mockPluginLinkExtension } from '../mocks';
@ -21,8 +21,8 @@ export function setupPluginsExtensionsHook() {
}));
}
export const plugins: PluginMeta[] = [
{
export const pluginMeta = {
[SupportedPlugin.Slo]: {
id: SupportedPlugin.Slo,
name: 'SLO dashboard',
type: PluginType.app,
@ -44,8 +44,28 @@ export const plugins: PluginMeta[] = [
},
module: 'public/plugins/grafana-slo-app/module.js',
baseUrl: 'public/plugins/grafana-slo-app',
},
{
} satisfies PluginMeta,
[SupportedPlugin.Irm]: {
id: SupportedPlugin.Irm,
name: 'Grafana IRM',
type: PluginType.app,
enabled: true,
info: {
author: { name: 'Grafana Labs', url: '' },
description: 'Grafana IRM',
links: [],
logos: {
small: 'public/plugins/grafana-irm-app/img/logo.svg',
large: 'public/plugins/grafana-irm-app/img/logo.svg',
},
screenshots: [],
version: 'local-dev',
updated: '2024-04-09',
},
module: 'public/plugins/grafana-irm-app/module.js',
baseUrl: 'public/plugins/grafana-irm-app',
} satisfies PluginMeta,
[SupportedPlugin.Incident]: {
id: SupportedPlugin.Incident,
name: 'Incident management',
type: PluginType.app,
@ -67,10 +87,10 @@ export const plugins: PluginMeta[] = [
},
module: 'public/plugins/grafana-incident-app/module.js',
baseUrl: 'public/plugins/grafana-incident-app',
},
{
id: 'grafana-asserts-app',
name: 'Asserts',
} satisfies PluginMeta,
[SupportedPlugin.OnCall]: {
id: SupportedPlugin.OnCall,
name: 'OnCall',
type: PluginType.app,
enabled: true,
info: {
@ -78,22 +98,22 @@ export const plugins: PluginMeta[] = [
name: 'Grafana Labs',
url: '',
},
description: 'Asserts',
description: 'OnCall',
links: [],
logos: {
small: 'public/plugins/grafana-asserts-app/img/logo.svg',
large: 'public/plugins/grafana-asserts-app/img/logo.svg',
small: '',
large: '',
},
screenshots: [],
version: 'local-dev',
updated: '2024-04-09',
},
module: 'public/plugins/grafana-asserts-app/module.js',
baseUrl: 'public/plugins/grafana-asserts-app',
},
{
id: SupportedPlugin.OnCall,
name: 'OnCall',
module: 'public/plugins/grafana-oncall-app/module.js',
baseUrl: 'public/plugins/grafana-oncall-app',
} satisfies PluginMeta,
['grafana-asserts-app']: {
id: 'grafana-asserts-app',
name: 'Asserts',
type: PluginType.app,
enabled: true,
info: {
@ -101,17 +121,48 @@ export const plugins: PluginMeta[] = [
name: 'Grafana Labs',
url: '',
},
description: 'OnCall',
description: 'Asserts',
links: [],
logos: {
small: '',
large: '',
small: 'public/plugins/grafana-asserts-app/img/logo.svg',
large: 'public/plugins/grafana-asserts-app/img/logo.svg',
},
screenshots: [],
version: 'local-dev',
updated: '2024-04-09',
},
module: 'public/plugins/grafana-oncall-app/module.js',
baseUrl: 'public/plugins/grafana-oncall-app',
},
module: 'public/plugins/grafana-asserts-app/module.js',
baseUrl: 'public/plugins/grafana-asserts-app',
} satisfies PluginMeta,
};
export const plugins: PluginMeta[] = [
pluginMeta[SupportedPlugin.Slo],
pluginMeta[SupportedPlugin.Incident],
pluginMeta[SupportedPlugin.OnCall],
pluginMeta['grafana-asserts-app'],
];
export function pluginMetaToPluginConfig(pluginMeta: PluginMeta): AppPluginConfig {
return {
id: pluginMeta.id,
path: pluginMeta.baseUrl,
preload: true,
version: pluginMeta.info.version,
angular: { detected: false, hideDeprecation: false },
loadingStrategy: PluginLoadingStrategy.script,
dependencies: {
plugins: [],
grafanaVersion: 'local-dev',
extensions: {
exposedComponents: [],
},
},
extensions: {
addedLinks: [],
addedComponents: [],
extensionPoints: [],
exposedComponents: [],
},
};
}

@ -1,6 +1,7 @@
export enum SupportedPlugin {
Incident = 'grafana-incident-app',
OnCall = 'grafana-oncall-app',
Irm = 'grafana-irm-app',
MachineLearning = 'grafana-ml-app',
Labels = 'grafana-labels-app',
Slo = 'grafana-slo-app',

@ -1,6 +1,14 @@
import { config } from '@grafana/runtime';
import { checkEvaluationIntervalGlobalLimit } from './config';
import { pluginMeta, pluginMetaToPluginConfig } from '../testSetup/plugins';
import { SupportedPlugin } from '../types/pluginBridges';
import {
checkEvaluationIntervalGlobalLimit,
getIrmIfPresentOrIncidentPluginId,
getIrmIfPresentOrOnCallPluginId,
getIsIrmPluginPresent,
} from './config';
describe('checkEvaluationIntervalGlobalLimit', () => {
it('should NOT exceed limit if evaluate every is not valid duration', () => {
@ -51,3 +59,48 @@ describe('checkEvaluationIntervalGlobalLimit', () => {
expect(exceedsLimit).toBe(false);
});
});
describe('getIsIrmPluginPresent', () => {
it('should return true when IRM plugin is present in config.apps', () => {
config.apps = { [SupportedPlugin.Irm]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.Irm]) };
expect(getIsIrmPluginPresent()).toBe(true);
});
it('should return false when IRM plugin is not present in config.apps', () => {
config.apps = {
[SupportedPlugin.OnCall]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.OnCall]),
[SupportedPlugin.Incident]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.Incident]),
};
expect(getIsIrmPluginPresent()).toBe(false);
});
});
describe('getIrmIfPresentOrIncidentPluginId', () => {
it('should return IRM plugin ID when IRM plugin is present', () => {
config.apps = { [SupportedPlugin.Irm]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.Irm]) };
expect(getIrmIfPresentOrIncidentPluginId()).toBe(SupportedPlugin.Irm);
});
it('should return Incident plugin ID when IRM plugin is not present', () => {
config.apps = {
[SupportedPlugin.OnCall]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.OnCall]),
[SupportedPlugin.Incident]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.Incident]),
};
expect(getIrmIfPresentOrIncidentPluginId()).toBe(SupportedPlugin.Incident);
});
});
describe('getIrmIfPresentOrOnCallPluginId', () => {
it('should return IRM plugin ID when IRM plugin is present', () => {
config.apps = { [SupportedPlugin.Irm]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.Irm]) };
expect(getIrmIfPresentOrOnCallPluginId()).toBe(SupportedPlugin.Irm);
});
it('should return OnCall plugin ID when IRM plugin is not present', () => {
config.apps = {
[SupportedPlugin.OnCall]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.OnCall]),
[SupportedPlugin.Incident]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.Incident]),
};
expect(getIrmIfPresentOrOnCallPluginId()).toBe(SupportedPlugin.OnCall);
});
});

@ -1,6 +1,8 @@
import { DataSourceInstanceSettings, DataSourceJsonData } from '@grafana/data';
import { config } from '@grafana/runtime';
import { SupportedPlugin } from '../types/pluginBridges';
import { isValidPrometheusDuration, safeParsePrometheusDuration } from './time';
export function getAllDataSources(): Array<DataSourceInstanceSettings<DataSourceJsonData>> {
@ -26,3 +28,15 @@ export function checkEvaluationIntervalGlobalLimit(alertGroupEvaluateEvery?: str
return { globalLimit: evaluateEveryGlobalLimitMs, exceedsLimit };
}
export function getIsIrmPluginPresent() {
return SupportedPlugin.Irm in config.apps;
}
export function getIrmIfPresentOrIncidentPluginId() {
return getIsIrmPluginPresent() ? SupportedPlugin.Irm : SupportedPlugin.Incident;
}
export function getIrmIfPresentOrOnCallPluginId() {
return getIsIrmPluginPresent() ? SupportedPlugin.Irm : SupportedPlugin.OnCall;
}

@ -44,6 +44,7 @@ jest.mock('@grafana/runtime', () => ({
featureToggles: {
newDashboardWithFiltersAndGroupBy: false,
},
apps: {},
bootData: {
...jest.requireActual('@grafana/runtime').config.bootData,
user: {

@ -36,6 +36,7 @@ jest.mock('@grafana/runtime', () => ({
config: {
buildInfo: {},
panels: {},
apps: {},
featureToggles: {
newVariables: false,
},

@ -32,6 +32,7 @@ jest.mock('@grafana/runtime', () => ({
featureToggles: {
accessControlOnCall: true,
},
apps: {},
theme2: {
breakpoints: {
values: {

@ -0,0 +1,44 @@
<svg width="14" height="26" viewBox="0 0 14 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.91383 2.50082C7.65753 2.50082 8.26043 1.94099 8.26043 1.25041C8.26043 0.559827 7.65753 0 6.91383 0C6.17012 0 5.56723 0.559827 5.56723 1.25041C5.56723 1.94099 6.17012 2.50082 6.91383 2.50082Z" fill="url(#paint0_linear_1761_13128)"/>
<path d="M7.76653 8.83258C7.62149 8.83258 7.50323 8.94239 7.50323 9.07707V14.7293C7.50323 14.864 7.62149 14.9738 7.76653 14.9738H9.54152C9.67652 14.9738 9.79032 14.8784 9.80371 14.7531L10.414 9.1009C10.4296 8.9569 10.308 8.83154 10.1518 8.83154H7.76541L7.76653 8.83258Z" fill="url(#paint1_linear_1761_13128)"/>
<path d="M3.82359 8.83301C3.66739 8.83301 3.54579 8.95837 3.56141 9.10236L4.16721 14.7545C4.1806 14.88 4.29439 14.9753 4.42939 14.9753H6.18655C6.33158 14.9753 6.44984 14.8654 6.44984 14.7308V9.07854C6.44984 8.94386 6.33158 8.83405 6.18655 8.83405L3.82359 8.83301Z" fill="url(#paint2_linear_1761_13128)"/>
<path d="M13.7109 8.83301H11.7428C11.6078 8.83301 11.494 8.92832 11.4806 9.05367L10.8704 14.7059C10.8547 14.8498 10.9764 14.9753 11.1325 14.9753H12.4992C12.6253 14.9753 12.7335 14.8923 12.7581 14.7785L13.9707 9.12618C14.0032 8.97494 13.8782 8.83405 13.712 8.83405L13.7109 8.83301Z" fill="url(#paint3_linear_1761_13128)"/>
<path d="M3.1033 14.7059L2.4975 9.05367C2.48411 8.92832 2.37031 8.83301 2.23532 8.83301H0.263956C0.0977241 8.83301 -0.02723 8.9739 0.005125 9.12515L1.21784 14.7774C1.24239 14.8923 1.3506 14.9742 1.47667 14.9742H2.84112C2.9973 14.9742 3.11892 14.8498 3.1033 14.7059Z" fill="url(#paint4_linear_1761_13128)"/>
<path d="M1.77308 4.91963H12.0561C12.2859 4.91963 12.4889 4.7808 12.5581 4.57672C12.6641 4.26179 12.4109 3.9427 12.0561 3.9427H7.30895V2.94922C7.18176 2.97511 7.05012 2.98858 6.91402 2.98858C6.7779 2.98858 6.64626 2.97407 6.51907 2.94922V3.9427H1.77308C1.41829 3.9427 1.16504 4.26179 1.27103 4.57672C1.3402 4.7808 1.54326 4.91963 1.77308 4.91963Z" fill="url(#paint5_linear_1761_13128)"/>
<path d="M11.5082 7.68505L12.0025 6.21501C12.056 6.05755 11.9288 5.89697 11.7514 5.89697H2.07091C1.89352 5.89697 1.76633 6.05755 1.81989 6.21501L2.31413 7.68505C2.34871 7.78762 2.45023 7.85702 2.56515 7.85702H11.2561C11.371 7.85702 11.4748 7.78762 11.5082 7.68505Z" fill="url(#paint6_linear_1761_13128)"/>
<path d="M10.925 15.9541H2.90006C2.60888 15.9541 2.37347 16.1728 2.37347 16.4432V24.5319C2.37347 25.3431 3.0808 25.9998 3.95436 25.9998H9.87068C10.7442 25.9998 11.4516 25.3431 11.4516 24.5319V16.4432C11.4516 16.1728 11.2162 15.9541 10.925 15.9541Z" fill="url(#paint7_linear_1761_13128)"/>
<defs>
<linearGradient id="paint0_linear_1761_13128" x1="-0.310034" y1="-7.97382" x2="23.3982" y2="27.1329" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFEA00"/>
<stop offset="1" stop-color="#E95A28"/>
</linearGradient>
<linearGradient id="paint1_linear_1761_13128" x1="-4.43094" y1="-5.19439" x2="19.2773" y2="29.9123" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFEA00"/>
<stop offset="1" stop-color="#E95A28"/>
</linearGradient>
<linearGradient id="paint2_linear_1761_13128" x1="-6.81976" y1="-3.57993" x2="16.8884" y2="31.5258" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFEA00"/>
<stop offset="1" stop-color="#E95A28"/>
</linearGradient>
<linearGradient id="paint3_linear_1761_13128" x1="-2.16607" y1="-6.72202" x2="21.5422" y2="28.3837" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFEA00"/>
<stop offset="1" stop-color="#E95A28"/>
</linearGradient>
<linearGradient id="paint4_linear_1761_13128" x1="-9.07742" y1="-2.05499" x2="14.6307" y2="33.0507" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFEA00"/>
<stop offset="1" stop-color="#E95A28"/>
</linearGradient>
<linearGradient id="paint5_linear_1761_13128" x1="-1.93983" y1="-6.8738" x2="21.7683" y2="28.2329" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFEA00"/>
<stop offset="1" stop-color="#E95A28"/>
</linearGradient>
<linearGradient id="paint6_linear_1761_13128" x1="-3.03433" y1="-6.1368" x2="20.6739" y2="28.9689" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFEA00"/>
<stop offset="1" stop-color="#E95A28"/>
</linearGradient>
<linearGradient id="paint7_linear_1761_13128" x1="-10.2479" y1="-1.26362" x2="13.4602" y2="33.8431" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFEA00"/>
<stop offset="1" stop-color="#E95A28"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

Loading…
Cancel
Save