diff --git a/apps/chat/src/components/Marketplace/ApplicationCard.tsx b/apps/chat/src/components/Marketplace/ApplicationCard.tsx
index eb7ebf0552..ed2988046e 100644
--- a/apps/chat/src/components/Marketplace/ApplicationCard.tsx
+++ b/apps/chat/src/components/Marketplace/ApplicationCard.tsx
@@ -1,39 +1,54 @@
import {
IconBookmark,
IconBookmarkFilled,
+ IconLoader,
IconPencilMinus,
+ IconPlayerPlay,
+ IconPlaystationSquare,
IconTrashX,
IconWorldShare,
TablerIconsProps,
} from '@tabler/icons-react';
-import { useMemo } from 'react';
+import React, { useCallback, useMemo } from 'react';
import { useTranslation } from 'next-i18next';
import classNames from 'classnames';
-import { getModelShortDescription } from '@/src/utils/app/application';
+import {
+ getApplicationNextStatus,
+ getApplicationSimpleStatus,
+ getModelShortDescription,
+ isApplicationStatusUpdating,
+} from '@/src/utils/app/application';
import { getRootId } from '@/src/utils/app/id';
import { isMediumScreen } from '@/src/utils/app/mobile';
import { isEntityPublic } from '@/src/utils/app/publications';
+import {
+ ApplicationStatus,
+ SimpleApplicationStatus,
+} from '@/src/types/applications';
import { FeatureType } from '@/src/types/common';
import { DisplayMenuItemProps } from '@/src/types/menu';
import { DialAIEntityModel } from '@/src/types/models';
import { Translation } from '@/src/types/translation';
-import { useAppSelector } from '@/src/store/hooks';
+import { ApplicationActions } from '@/src/store/application/application.reducers';
+import { useAppDispatch, useAppSelector } from '@/src/store/hooks';
import { ModelsSelectors } from '@/src/store/models/models.reducers';
+import { SettingsSelectors } from '@/src/store/settings/settings.reducers';
import { ModelIcon } from '@/src/components/Chatbar/ModelIcon';
import ContextMenu from '@/src/components/Common/ContextMenu';
import { EntityMarkdownDescription } from '@/src/components/Common/MarkdownDescription';
import { ApplicationTopic } from '@/src/components/Marketplace/ApplicationTopic';
+import { FunctionStatusIndicator } from '@/src/components/Marketplace/FunctionStatusIndicator';
import Tooltip from '../Common/Tooltip';
import UnpublishIcon from '@/public/images/icons/unpublish.svg';
-import { PublishActions } from '@epam/ai-dial-shared';
+import { Feature, PublishActions } from '@epam/ai-dial-shared';
const DESKTOP_ICON_SIZE = 80;
const SMALL_ICON_SIZE = 48;
@@ -63,6 +78,22 @@ const CardFooter = ({ entity }: CardFooterProps) => {
);
};
+const getPlayerCaption = (entity: DialAIEntityModel) => {
+ switch (entity.functionStatus) {
+ case ApplicationStatus.STARTED:
+ return 'Stop';
+ case ApplicationStatus.CREATED:
+ case ApplicationStatus.STOPPED:
+ case ApplicationStatus.FAILED:
+ return 'Start';
+ case ApplicationStatus.STOPPING:
+ return 'Stopping';
+ case ApplicationStatus.STARTING:
+ default:
+ return 'Starting';
+ }
+};
+
interface ApplicationCardProps {
entity: DialAIEntityModel;
onClick: (entity: DialAIEntityModel) => void;
@@ -87,17 +118,66 @@ export const ApplicationCard = ({
const installedModelIds = useAppSelector(
ModelsSelectors.selectInstalledModelIds,
);
+ const dispatch = useAppDispatch();
+
+ const isCodeAppsEnabled = useAppSelector((state) =>
+ SettingsSelectors.isFeatureEnabled(state, Feature.CodeApps),
+ );
const isMyEntity = entity.id.startsWith(
getRootId({ featureType: FeatureType.Application }),
);
+ const isModifyDisabled = isApplicationStatusUpdating(entity);
+ const playerStatus = getApplicationSimpleStatus(entity);
+
+ const PlayerIcon = useMemo(() => {
+ switch (playerStatus) {
+ case SimpleApplicationStatus.START:
+ return IconPlayerPlay;
+ case SimpleApplicationStatus.STOP:
+ return IconPlaystationSquare;
+ case SimpleApplicationStatus.UPDATING:
+ default:
+ return IconLoader;
+ }
+ }, [playerStatus]);
+
+ const handleUpdateFunctionStatus = useCallback(() => {
+ dispatch(
+ ApplicationActions.startUpdatingFunctionStatus({
+ id: entity.id,
+ status: getApplicationNextStatus(entity),
+ }),
+ );
+ }, [dispatch, entity]);
const menuItems: DisplayMenuItemProps[] = useMemo(
() => [
+ {
+ name: t(getPlayerCaption(entity)),
+ dataQa: 'status-change',
+ disabled: playerStatus === SimpleApplicationStatus.UPDATING,
+ display: isMyEntity && !!entity.functionStatus && isCodeAppsEnabled,
+ Icon: (props: TablerIconsProps) => (
+
+ ),
+ onClick: (e: React.MouseEvent) => {
+ e.stopPropagation();
+ handleUpdateFunctionStatus();
+ },
+ },
{
name: t('Edit'),
dataQa: 'edit',
display: isMyEntity && !!onEdit,
+ disabled: isModifyDisabled,
Icon: IconPencilMinus,
onClick: (e: React.MouseEvent) => {
e.stopPropagation();
@@ -128,6 +208,7 @@ export const ApplicationCard = ({
name: t('Delete'),
dataQa: 'delete',
display: isMyEntity && !!onDelete,
+ disabled: isModifyDisabled,
Icon: (props: TablerIconsProps) => (
),
@@ -137,7 +218,19 @@ export const ApplicationCard = ({
},
},
],
- [entity, onPublish, t, onDelete, isMyEntity, onEdit],
+ [
+ entity,
+ onPublish,
+ t,
+ onDelete,
+ isMyEntity,
+ onEdit,
+ isModifyDisabled,
+ playerStatus,
+ PlayerIcon,
+ isCodeAppsEnabled,
+ handleUpdateFunctionStatus,
+ ],
);
const iconSize =
@@ -197,7 +290,7 @@ export const ApplicationCard = ({
{entity.version}