Skip to content

Commit

Permalink
[GEN-1672]: add logic for "go to details" button of toast notificatio…
Browse files Browse the repository at this point in the history
…ns (#1702)
  • Loading branch information
BenElferink authored Nov 7, 2024
1 parent d9292ff commit e15ea9a
Show file tree
Hide file tree
Showing 26 changed files with 370 additions and 336 deletions.
4 changes: 2 additions & 2 deletions frontend/webapp/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
'use client';
import { useEffect } from 'react';
import { ROUTES, CONFIG } from '@/utils';
import { useRouter } from 'next/navigation';
import { useConfig, useNotify } from '@/hooks';
import { Loader } from '@keyval-dev/design-system';
import { ROUTES, CONFIG, NOTIFICATION } from '@/utils';

export default function App() {
const router = useRouter();
Expand All @@ -13,7 +13,7 @@ export default function App() {
useEffect(() => {
if (error) {
notify({
type: 'error',
type: NOTIFICATION.ERROR,
title: error.name,
message: error.message,
});
Expand Down
1 change: 1 addition & 0 deletions frontend/webapp/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from './destinations';
export * from './main';
export * from './modals';
export * from './notification/notification-list'; // old
export * from './notification/toast-list'; // new
116 changes: 116 additions & 0 deletions frontend/webapp/components/notification/toast-list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import React from 'react';
import styled from 'styled-components';
import { NotificationNote } from '@/reuseable-components';
import { Notification, OVERVIEW_ENTITY_TYPES } from '@/types';
import { DrawerBaseItem, useDrawerStore, useNotificationStore } from '@/store';
import { useActualDestination, useActualSources, useGetActions, useGetInstrumentationRules } from '@/hooks';
import { getIdFromSseTarget } from '@/utils';

const Container = styled.div`
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
z-index: 10000;
display: flex;
flex-direction: column;
gap: 6px;
min-width: 600px;
`;

export const ToastList: React.FC = () => {
const { notifications } = useNotificationStore();

return (
<Container>
{notifications
.filter(({ dismissed }) => !dismissed)
.map((notif) => (
<Toast key={`toast-${notif.id}`} {...notif} />
))}
</Container>
);
};

const Toast: React.FC<Notification> = ({ id, type, title, message, crdType, target }) => {
const { markAsDismissed, markAsSeen } = useNotificationStore();

const { actions } = useGetActions();
const { sources } = useActualSources();
const { destinations } = useActualDestination();
const { instrumentationRules } = useGetInstrumentationRules();
const setSelectedItem = useDrawerStore(({ setSelectedItem }) => setSelectedItem);

const onClick = () => {
if (crdType && target) {
const drawerItem: Partial<DrawerBaseItem> = {};

switch (crdType) {
case OVERVIEW_ENTITY_TYPES.RULE:
drawerItem['type'] = OVERVIEW_ENTITY_TYPES.RULE;
drawerItem['id'] = getIdFromSseTarget(target, OVERVIEW_ENTITY_TYPES.RULE);
drawerItem['item'] = instrumentationRules.find((item) => item.ruleId === drawerItem['id']);
break;

case OVERVIEW_ENTITY_TYPES.SOURCE:
case 'InstrumentedApplication':
case 'InstrumentationInstance':
drawerItem['type'] = OVERVIEW_ENTITY_TYPES.SOURCE;
drawerItem['id'] = getIdFromSseTarget(target, OVERVIEW_ENTITY_TYPES.SOURCE);
drawerItem['item'] = sources.find(
(item) =>
item.kind === drawerItem['id']?.['kind'] &&
item.name === drawerItem['id']?.['name'] &&
item.namespace === drawerItem['id']?.['namespace']
);
break;

case OVERVIEW_ENTITY_TYPES.ACTION:
drawerItem['type'] = OVERVIEW_ENTITY_TYPES.ACTION;
drawerItem['id'] = getIdFromSseTarget(target, OVERVIEW_ENTITY_TYPES.ACTION);
drawerItem['item'] = actions.find((item) => item.id === drawerItem['id']);
break;

case OVERVIEW_ENTITY_TYPES.DESTINATION:
case 'Destination':
drawerItem['type'] = OVERVIEW_ENTITY_TYPES.DESTINATION;
drawerItem['id'] = getIdFromSseTarget(target, OVERVIEW_ENTITY_TYPES.DESTINATION);
drawerItem['item'] = destinations.find((item) => item.id === drawerItem['id']);
break;

default:
break;
}

if (!!drawerItem.item) {
setSelectedItem(drawerItem as DrawerBaseItem);
}
}

markAsSeen(id);
markAsDismissed(id);
};

const onClose = ({ asSeen }) => {
markAsDismissed(id);
if (asSeen) markAsSeen(id);
};

return (
<NotificationNote
id={id}
type={type}
title={title}
message={message}
action={
crdType && target
? {
label: 'go to details',
onClick,
}
: undefined
}
onClose={onClose}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@ import { useDrawerStore } from '@/store';
import { CardDetails } from '@/components';
import type { ActionDataParsed } from '@/types';
import { ChooseActionBody } from '../choose-action-body';
import { useActionCRUD, useActionFormData } from '@/hooks';
import OverviewDrawer from '../../overview/overview-drawer';
import buildCardFromActionSpec from './build-card-from-action-spec';
import { useActionCRUD, useActionFormData, useNotify } from '@/hooks';
import { ACTION_OPTIONS } from '../choose-action-modal/action-options';

interface Props {}

const ActionDrawer: React.FC<Props> = () => {
const notify = useNotify();
const selectedItem = useDrawerStore(({ selectedItem }) => selectedItem);
const [isEditing, setIsEditing] = useState(false);

Expand Down Expand Up @@ -68,13 +67,7 @@ const ActionDrawer: React.FC<Props> = () => {
};

const handleSave = async (newTitle: string) => {
if (!validateForm()) {
notify({
type: 'error',
title: 'Update',
message: 'Required fields are missing!',
});
} else {
if (validateForm({ withAlert: true })) {
const payload = {
...formData,
name: newTitle,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { ChooseActionBody } from '../';
import React, { useMemo, useState } from 'react';
import { CenterThis, ModalBody } from '@/styles';
import { useActionCRUD, useActionFormData } from '@/hooks/actions';
import { ACTION_OPTIONS, type ActionOption } from './action-options';
import { AutocompleteInput, Modal, NavigationButtons, Divider, FadeLoader, SectionTitle, ModalContent, Center } from '@/reuseable-components';
import { AutocompleteInput, Modal, NavigationButtons, Divider, FadeLoader, SectionTitle } from '@/reuseable-components';

interface AddActionModalProps {
isOpen: boolean;
Expand Down Expand Up @@ -50,7 +51,7 @@ export const AddActionModal: React.FC<AddActionModalProps> = ({ isOpen, onClose
/>
}
>
<ModalContent>
<ModalBody>
<SectionTitle
title='Define Action'
description='Actions are a way to modify the OpenTelemetry data recorded by Odigos sources before it is exported to your Odigos destinations. Choose an action type and provide necessary information.'
Expand All @@ -62,15 +63,15 @@ export const AddActionModal: React.FC<AddActionModalProps> = ({ isOpen, onClose
<Divider margin='16px 0' />

{loading ? (
<Center>
<CenterThis>
<FadeLoader cssOverride={{ scale: 2 }} />
</Center>
</CenterThis>
) : (
<ChooseActionBody action={selectedItem} formData={formData} handleFormChange={handleFormChange} />
)}
</div>
) : null}
</ModalContent>
</ModalBody>
</Modal>
);
};
Original file line number Diff line number Diff line change
@@ -1,18 +1,9 @@
import { CenterThis, ModalBody } from '@/styles';
import { ChooseRuleBody } from '../choose-rule-body';
import { RULE_OPTIONS, RuleOption } from './rule-options';
import React, { useEffect, useMemo, useState } from 'react';
import React, { useMemo, useState } from 'react';
import { useInstrumentationRuleCRUD, useInstrumentationRuleFormData } from '@/hooks';
import {
AutocompleteInput,
Center,
Divider,
FadeLoader,
Modal,
ModalContent,
NavigationButtons,
NotificationNote,
SectionTitle,
} from '@/reuseable-components';
import { AutocompleteInput, Divider, FadeLoader, Modal, NavigationButtons, NotificationNote, SectionTitle } from '@/reuseable-components';

interface Props {
isOpen: boolean;
Expand All @@ -22,11 +13,7 @@ interface Props {
export const AddRuleModal: React.FC<Props> = ({ isOpen, onClose }) => {
const { formData, handleFormChange, resetFormData, validateForm } = useInstrumentationRuleFormData();
const { createInstrumentationRule, loading } = useInstrumentationRuleCRUD({ onSuccess: handleClose });
const [selectedItem, setSelectedItem] = useState<RuleOption | undefined>(undefined);

useEffect(() => {
if (!selectedItem) handleSelect(RULE_OPTIONS[0]);
}, [selectedItem]);
const [selectedItem, setSelectedItem] = useState<RuleOption | undefined>(RULE_OPTIONS[0]);

const isFormOk = useMemo(() => !!selectedItem && validateForm(), [selectedItem, formData]);

Expand Down Expand Up @@ -63,7 +50,7 @@ export const AddRuleModal: React.FC<Props> = ({ isOpen, onClose }) => {
/>
}
>
<ModalContent>
<ModalBody>
<SectionTitle
title='Define Instrumentation Rule'
description='Instrumentation rules control how telemetry is recorded from your application. Choose a rule type and provide necessary information.'
Expand All @@ -86,15 +73,15 @@ export const AddRuleModal: React.FC<Props> = ({ isOpen, onClose }) => {
<Divider margin='16px 0' />

{loading ? (
<Center>
<CenterThis>
<FadeLoader cssOverride={{ scale: 2 }} />
</Center>
</CenterThis>
) : (
<ChooseRuleBody rule={selectedItem} formData={formData} handleFormChange={handleFormChange} />
)}
</div>
) : null}
</ModalContent>
</ModalBody>
</Modal>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@ import type { InstrumentationRuleSpec } from '@/types';
import OverviewDrawer from '../../overview/overview-drawer';
import { RULE_OPTIONS } from '../add-rule-modal/rule-options';
import buildCardFromRuleSpec from './build-card-from-rule-spec';
import { useInstrumentationRuleCRUD, useInstrumentationRuleFormData, useNotify } from '@/hooks';
import { useInstrumentationRuleCRUD, useInstrumentationRuleFormData } from '@/hooks';

interface Props {}

const RuleDrawer: React.FC<Props> = () => {
const notify = useNotify();
const selectedItem = useDrawerStore(({ selectedItem }) => selectedItem);
const [isEditing, setIsEditing] = useState(false);

Expand Down Expand Up @@ -66,13 +65,7 @@ const RuleDrawer: React.FC<Props> = () => {
};

const handleSave = async (newTitle: string) => {
if (!validateForm()) {
notify({
type: 'error',
title: 'Update',
message: 'Required fields are missing!',
});
} else {
if (validateForm({ withAlert: true })) {
const payload = {
...formData,
ruleName: newTitle,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
import React, { useMemo } from 'react';
import dynamic from 'next/dynamic';
import styled from 'styled-components';
import { ToastList } from '@/components';
import { OverviewActionMenuContainer } from '../overview-actions-menu';
import { buildNodesAndEdges, NodeBaseDataFlow, ToastList } from '@/reuseable-components';
import { buildNodesAndEdges, NodeBaseDataFlow, } from '@/reuseable-components';
import {
useMetrics,
useGetActions,
Expand All @@ -14,6 +15,7 @@ import {
useGetInstrumentationRules,
} from '@/hooks';


const AllDrawers = dynamic(() => import('../all-drawers'), {
ssr: false,
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
import styled from 'styled-components';
import React, { useState, useCallback } from 'react';
import { useActualSources, useConnectSourcesMenuState } from '@/hooks';
import { ChooseSourcesBody } from '../choose-sources-body';
import { Modal, NavigationButtons } from '@/reuseable-components';
import { K8sActualSource, PersistNamespaceItemInput } from '@/types';

const ChooseSourcesBodyWrapper = styled.div`
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
`;
import { useActualSources, useConnectSourcesMenuState } from '@/hooks';

interface AddSourceModalProps {
isOpen: boolean;
Expand Down Expand Up @@ -65,9 +57,7 @@ export const AddSourceModal: React.FC<AddSourceModalProps> = ({ isOpen, onClose
/>
}
>
<ChooseSourcesBodyWrapper>
<ChooseSourcesBody isModal stateMenu={stateMenu} sourcesList={sourcesList} stateHandlers={stateHandlers} setSourcesList={setSourcesList} />
</ChooseSourcesBodyWrapper>
<ChooseSourcesBody isModal stateMenu={stateMenu} sourcesList={sourcesList} stateHandlers={stateHandlers} setSourcesList={setSourcesList} />
</Modal>
);
};
Loading

0 comments on commit e15ea9a

Please sign in to comment.