Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import Tabs from '@/src/components/Common/Tabs/Tabs';
import { ButtonsI18nKey, TabsI18nKey } from '@/src/constants/i18n';
import { BASE_ICON_PROPS } from '@/src/constants/main-layout';
import { useI18n } from '@/src/locales/client';
import { DialRoute } from '@/src/models/dial/route';
import { DialAppRoute } from '@/src/models/dial/route';
import { TabModel } from '@/src/models/tab';
import { PopUpState } from '@/src/types/pop-up';
import { TabOrientation } from '@/src/types/tab';
import CreateRoute from './CreateRoute';
import RouteContent from './RouteContent';
import CreateRoute from '@/src/components/EntityView/AppRoute/CreateRoute';
import RouteContent from '@/src/components/EntityView/AppRoute/Content/RouteContent';

interface Props {
routes?: DialRoute[];
onChangeRoutes: (routes: DialRoute[]) => void;
routes?: DialAppRoute[];
onChangeRoutes: (routes: DialAppRoute[]) => void;
}

const EntityRoutes: FC<Props> = ({ routes, onChangeRoutes }) => {
Expand Down Expand Up @@ -49,7 +49,7 @@ const EntityRoutes: FC<Props> = ({ routes, onChangeRoutes }) => {
}, []);

const onChangeRoute = useCallback(
(route: DialRoute) => {
(route: DialAppRoute) => {
if (routes) {
routes[activeRouteIndex as number] = route;
onChangeRoutes([...(routes || [])]);
Expand All @@ -61,7 +61,7 @@ const EntityRoutes: FC<Props> = ({ routes, onChangeRoutes }) => {
const onCreate = useCallback(
(name: string) => {
handleModalClose();
onChangeRoutes([...(routes || []), { name } as DialRoute]);
onChangeRoutes([...(routes || []), { name } as DialAppRoute]);
},
[handleModalClose, onChangeRoutes, routes],
);
Expand Down Expand Up @@ -93,7 +93,7 @@ const EntityRoutes: FC<Props> = ({ routes, onChangeRoutes }) => {
<div className="flex flex-col flex-1 min-h-0 min-w-0 relative">
{routes?.[activeRouteIndex as number] && (
<RouteContent
route={routes?.[activeRouteIndex as number] || ({} as DialRoute)}
route={routes?.[activeRouteIndex as number] || ({} as DialAppRoute)}
onChangeRoute={onChangeRoute}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
'use client';
import { FC, useCallback } from 'react';

import Paths from '@/src/components/Routes/Paths/Paths';
import { RoutesI18nKey } from '@/src/constants/i18n';
import { useI18n } from '@/src/locales/client';
import { AttachmentPaths, DialAppRoute } from '@/src/models/dial/route';

interface Props {
route: DialAppRoute;
onChangeRoute: (route: DialAppRoute) => void;
}

const RouteAttachments: FC<Props> = ({ route, onChangeRoute }) => {
const t = useI18n() as (str: string) => string;

const onChangeRequest = useCallback(
(paths: string[]) => {
onChangeRoute({
...route,
attachmentPaths: { ...(route.attachmentPaths || {}), requestBody: paths } as AttachmentPaths,
});
},
[route, onChangeRoute],
);

const onChangeResponse = useCallback(
(paths: string[]) => {
onChangeRoute({
...route,
attachmentPaths: { ...(route.attachmentPaths || {}), responseBody: paths } as AttachmentPaths,
});
},
[route, onChangeRoute],
);

return (
<div className="h-full w-full flex flex-col divide-y gap-y-9 divide-primary">
<div className="w-full lg:w-[50%]">
<Paths
title={t(RoutesI18nKey.RequestAttachmentPaths)}
paths={route.attachmentPaths?.requestBody}
onChangePaths={onChangeRequest}
/>
</div>
<div className="w-full lg:w-[50%] pt-9">
<Paths
title={t(RoutesI18nKey.ResponseAttachmentPaths)}
paths={route.attachmentPaths?.responseBody}
onChangePaths={onChangeResponse}
/>
</div>
</div>
);
};

export default RouteAttachments;
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,21 @@
import { FC, useState } from 'react';

import Tabs from '@/src/components/Common/Tabs/Tabs';
import { EntityViewTab, propertiesTabs } from '@/src/components/EntityView/View/utils';
import { attachmentsTabs, EntityViewTab, propertiesTabs } from '@/src/components/EntityView/View/utils';
import { useI18n } from '@/src/locales/client';
import { DialRoute } from '@/src/models/dial/route';
import { DialAppRoute } from '@/src/models/dial/route';
import RouteProperties from '@/src/components/Routes/Properties/RouteProperties';
import RouteAttachments from './RouteAttachments';

interface Props {
route: DialRoute;
onChangeRoute: (route: DialRoute) => void;
route: DialAppRoute;
onChangeRoute: (route: DialAppRoute) => void;
}

const RouteContent: FC<Props> = ({ route, onChangeRoute }) => {
const t = useI18n() as (stringToTranslate: string) => string;

const tabs = [propertiesTabs(t)];
const tabs = [propertiesTabs(t), attachmentsTabs(t)];

const [activeTab, setActiveTab] = useState(EntityViewTab.Properties);

Expand All @@ -29,6 +30,8 @@ const RouteContent: FC<Props> = ({ route, onChangeRoute }) => {
{activeTab === EntityViewTab.Properties && (
<RouteProperties route={route} updateRoute={onChangeRoute} isAppRoute={true} />
)}

{activeTab === EntityViewTab.Attachments && <RouteAttachments route={route} onChangeRoute={onChangeRoute} />}
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { TabsI18nKey } from '@/src/constants/i18n';
import { TabModel } from '@/src/models/tab';
import { ApplicationRoute } from '@/src/types/routes';
import { DialApplication, DialApplicationScheme } from '../../../models/dial/application';
import { DialApplication, DialApplicationScheme } from '@/src/models/dial/application';

export enum EntityViewTab {
Properties = 'Properties',
Expand All @@ -20,6 +20,7 @@ export enum EntityViewTab {
Routes = 'Routes',
Traces = 'Traces',
Conversations = 'Conversations',
Attachments = 'Attachments',
}

export const propertiesTabs = (t: (stringToTranslate: string) => string) => ({
Expand Down Expand Up @@ -87,6 +88,11 @@ export const conversationsTabs = (t: (stringToTranslate: string) => string) => (
name: t(TabsI18nKey.Conversations),
});

export const attachmentsTabs = (t: (stringToTranslate: string) => string) => ({
id: EntityViewTab.Attachments,
name: t(TabsI18nKey.Attachments),
});

export const getViewTabs = (
t: (stringToTranslate: string) => string,
view: ApplicationRoute,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,12 @@ const MaxRetryAttempts: FC<Props> = ({ maxRetryAttempts, onChangeMaxRetryAttempt
const t = useI18n();

const items: DropdownItemsModel[] = [
{
id: '0',
name: t(BasicI18nKey.None),
},
{
id: '1',
name: '1',
},
{
id: '2',
name: '2',
},
{
id: '3',
name: '3',
},
{
id: '4',
name: '4',
},
{
id: '5',
name: '5',
},
{ id: '0', name: t(BasicI18nKey.None) },
{ id: '1', name: '1' },
{ id: '2', name: '2' },
{ id: '3', name: '3' },
{ id: '4', name: '4' },
{ id: '5', name: '5' },
];
const activeMaxAttempts = maxRetryAttempts?.toString() || '0';
const onChange = useCallback(
Expand Down
24 changes: 13 additions & 11 deletions apps/ai-dial-admin/src/components/Routes/Paths/Path.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,36 @@
import { FC, useEffect, useState } from 'react';
import { FC, useEffect, useMemo, useState } from 'react';

import { IconTrash } from '@tabler/icons-react';
import classNames from 'classnames';

import { TextInputField } from '@/src/components/Common/InputField/InputField';
import { EntityFieldsI18nKey, EntityPlaceholdersI18nKey, ErrorI18nKey } from '@/src/constants/i18n';
import { EntityPlaceholdersI18nKey, ErrorI18nKey } from '@/src/constants/i18n';
import { BASE_ICON_PROPS } from '@/src/constants/main-layout';
import { useI18n } from '@/src/locales/client';
import { isValidRoutePath } from '@/src/utils/validation/path-error';

interface Props {
index: number;
fieldTitle: string;
path: string;
allPaths?: string[];
onRemove: (index: number) => void;
onChangePath: (index: number, value: string) => void;
}

const Path: FC<Props> = ({ index, path, allPaths, onRemove, onChangePath }) => {
const Path: FC<Props> = ({ index, path, fieldTitle, allPaths, onRemove, onChangePath }) => {
const t = useI18n();

const [isEmptyPath, setIsEmptyPath] = useState(true);
const [isInvalidPath, setIsInvalidPath] = useState(false);
const isAllEmptyValues = !allPaths?.some((v) => v !== '');
const error = useMemo(() => {
return isEmptyPath && index === 0 && isAllEmptyValues
? t(ErrorI18nKey.RequiredProperty)
: isInvalidPath
? t(ErrorI18nKey.InvalidPath)
: '';
}, [index, isAllEmptyValues, isEmptyPath, isInvalidPath, t]);

const removeButtonClass = classNames(
'cursor-pointer ml-[10px]',
Expand All @@ -48,15 +56,9 @@ const Path: FC<Props> = ({ index, path, allPaths, onRemove, onChangePath }) => {
elementId={'path ' + index}
value={path}
placeholder={t(EntityPlaceholdersI18nKey.PathUrl)}
fieldTitle={index === 0 ? t(EntityFieldsI18nKey.paths) : ''}
fieldTitle={index === 0 ? fieldTitle : ''}
onChange={(value) => onChangePath(index, value)}
errorText={
isEmptyPath && index === 0 && isAllEmptyValues
? t(ErrorI18nKey.RequiredProperty)
: isInvalidPath
? t(ErrorI18nKey.InvalidPath)
: ''
}
errorText={error}
invalid={(isEmptyPath && index === 0 && isAllEmptyValues) || isInvalidPath}
/>
</div>
Expand Down
48 changes: 24 additions & 24 deletions apps/ai-dial-admin/src/components/Routes/Paths/Paths.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,51 +5,51 @@ import { EntityFieldsI18nKey, EntityPlaceholdersI18nKey, RoutesI18nKey } from '@

describe('Paths', () => {
test('renders empty path input if no paths', () => {
render(<Paths route={{ paths: [] }} updateRoute={vi.fn()} />);
render(<Paths paths={[]} title="title" onChangePaths={vi.fn()} />);
expect(screen.getByPlaceholderText(EntityPlaceholdersI18nKey.PathUrl)).toBeInTheDocument();
expect(screen.getByText(EntityFieldsI18nKey.paths)).toBeInTheDocument();
expect(screen.getByText('title')).toBeInTheDocument();
});

test('renders Path components for each path', () => {
render(<Paths route={{ paths: ['/a', '/b'] }} updateRoute={vi.fn()} />);
render(<Paths paths={['/a', '/b']} title="title" onChangePaths={vi.fn()} />);
expect(screen.getByDisplayValue('/a')).toBeInTheDocument();
expect(screen.getByDisplayValue('/b')).toBeInTheDocument();
});

test('calls updateRoute when AddPaths is clicked', () => {
const updateRoute = vi.fn();
render(<Paths route={{ paths: ['/a'] }} updateRoute={updateRoute} />);
fireEvent.click(screen.getByText(RoutesI18nKey.AddPaths));
expect(updateRoute).toHaveBeenCalledWith({ paths: ['/a', ''] });
test('calls onChangePaths when AddPaths is clicked', () => {
const onChangePaths = vi.fn();
render(<Paths paths={['/a']} title="title" onChangePaths={onChangePaths} />);
fireEvent.click(screen.getByRole('button', { name: RoutesI18nKey.AddPaths }));
expect(onChangePaths).toHaveBeenCalledWith(['/a', '']);
});

test('calls updateRoute with two empty paths if adding first path', () => {
const updateRoute = vi.fn();
render(<Paths route={{ paths: void 0 }} updateRoute={updateRoute} />);
test('calls onChangePaths with two empty paths if adding first path', () => {
const onChangePaths = vi.fn();
render(<Paths title="title" onChangePaths={onChangePaths} />);
fireEvent.click(screen.getByText(RoutesI18nKey.AddPaths));
expect(updateRoute).toHaveBeenCalledWith({ paths: ['', ''] });
expect(onChangePaths).toHaveBeenCalledWith(['', '']);
fireEvent.click(screen.getByLabelText('button'));
expect(updateRoute).toHaveBeenCalledWith({ paths: ['', ''] });
expect(onChangePaths).toHaveBeenCalledWith(['', '']);
});

test('calls updateRoute when path input changes', () => {
const updateRoute = vi.fn();
render(<Paths route={{ paths: ['/a'] }} updateRoute={updateRoute} />);
test('calls onChangePaths when path input changes', () => {
const onChangePaths = vi.fn();
render(<Paths paths={['/a']} title="title" onChangePaths={onChangePaths} />);
fireEvent.change(screen.getByDisplayValue('/a'), { target: { value: '/changed' } });
expect(updateRoute).toHaveBeenCalledWith({ paths: ['/changed'] });
expect(onChangePaths).toHaveBeenCalledWith(['/changed']);
});

test('calls updateRoute when Remove is clicked', () => {
const updateRoute = vi.fn();
render(<Paths route={{ paths: ['/a', '/b'] }} updateRoute={updateRoute} />);
test('calls onChangePaths when Remove is clicked', () => {
const onChangePaths = vi.fn();
render(<Paths paths={['/a', '/b']} title="title" onChangePaths={onChangePaths} />);
fireEvent.click(screen.getAllByLabelText('button')[0]);
expect(updateRoute).toHaveBeenCalledWith({ paths: ['/b'] });
expect(onChangePaths).toHaveBeenCalledWith(['/b']);
});

test('clears path if only one and Remove is clicked', () => {
const updateRoute = vi.fn();
render(<Paths route={{ paths: ['/a'] }} updateRoute={updateRoute} />);
const onChangePaths = vi.fn();
render(<Paths paths={['/a']} title="title" onChangePaths={onChangePaths} />);
fireEvent.click(screen.getByLabelText('button'));
expect(updateRoute).toHaveBeenCalledWith({ paths: [''] });
expect(onChangePaths).toHaveBeenCalledWith(['']);
});
});
Loading
Loading