From 757a7796e9d099541e269cdd1618fc66b15d87fb Mon Sep 17 00:00:00 2001 From: Magomed-Elbi Dzhukalaev Date: Mon, 16 Sep 2024 16:37:31 +0300 Subject: [PATCH 01/13] fix(chat): hide marketplace filters on minified sidebar --- .../Marketplace/MarketplaceFilterbar.tsx | 74 ++++++++++--------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/apps/chat/src/components/Marketplace/MarketplaceFilterbar.tsx b/apps/chat/src/components/Marketplace/MarketplaceFilterbar.tsx index e384cc5627..57ea25e777 100644 --- a/apps/chat/src/components/Marketplace/MarketplaceFilterbar.tsx +++ b/apps/chat/src/components/Marketplace/MarketplaceFilterbar.tsx @@ -142,42 +142,44 @@ const MarketplaceFilterbar = () => { Icon={IconHome} /> -
- - {openedSections[FilterTypes.ENTITY_TYPE] && ( -
- {entityTypes.map((type) => ( - - ))} -
- )} -
+ {showFilterbar && ( +
+ + {openedSections[FilterTypes.ENTITY_TYPE] && ( +
+ {entityTypes.map((type) => ( + + ))} +
+ )} +
+ )} ); }; From 38552fb4c1cf0be8e7bdadf251b6d09aef4899d4 Mon Sep 17 00:00:00 2001 From: Magomed-Elbi Dzhukalaev Date: Mon, 16 Sep 2024 18:47:03 +0300 Subject: [PATCH 02/13] feat(chat): add marketplace tabs --- apps/chat/src/constants/marketplace.ts | 5 +++++ .../store/marketplace/marketplace.reducers.ts | 18 ++++++++++-------- .../store/marketplace/marketplace.selectors.ts | 9 +++++++-- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/apps/chat/src/constants/marketplace.ts b/apps/chat/src/constants/marketplace.ts index bac2c322b3..014771d431 100644 --- a/apps/chat/src/constants/marketplace.ts +++ b/apps/chat/src/constants/marketplace.ts @@ -8,3 +8,8 @@ export enum FilterTypes { CAPABILITIES = 'Capabilities', ENVIRONMENT = 'Environment', } + +export enum MarketplaceTabs { + HOME = 'HOME', + MY_APPLICATIONS = 'MY_APPLICATIONS', +} diff --git a/apps/chat/src/store/marketplace/marketplace.reducers.ts b/apps/chat/src/store/marketplace/marketplace.reducers.ts index 144447b33d..e6f5a22d27 100644 --- a/apps/chat/src/store/marketplace/marketplace.reducers.ts +++ b/apps/chat/src/store/marketplace/marketplace.reducers.ts @@ -1,6 +1,6 @@ import { PayloadAction, createSlice } from '@reduxjs/toolkit'; -import { FilterTypes } from '@/src/constants/marketplace'; +import { FilterTypes, MarketplaceTabs } from '@/src/constants/marketplace'; import * as MarketplaceSelectors from './marketplace.selectors'; @@ -15,7 +15,8 @@ export interface MarketplaceState { [FilterTypes.CAPABILITIES]: string[]; [FilterTypes.ENVIRONMENT]: string[]; }; - searchQuery: string; + searchTerm: string; + selectedTab: MarketplaceTabs; } const initialState: MarketplaceState = { @@ -25,7 +26,8 @@ const initialState: MarketplaceState = { [FilterTypes.CAPABILITIES]: [], [FilterTypes.ENVIRONMENT]: [], }, - searchQuery: '', + searchTerm: '', + selectedTab: MarketplaceTabs.HOME, }; export const marketplaceSlice = createSlice({ @@ -41,11 +43,11 @@ export const marketplaceSlice = createSlice({ [payload.value], ); }, - setSearchQuery: ( - state, - { payload }: PayloadAction<{ searchQuery: string }>, - ) => { - state.searchQuery = payload.searchQuery; + setSearchTerm: (state, { payload }: PayloadAction) => { + state.searchTerm = payload; + }, + setSelectedTab: (state, { payload }: PayloadAction) => { + state.selectedTab = payload; }, }, }); diff --git a/apps/chat/src/store/marketplace/marketplace.selectors.ts b/apps/chat/src/store/marketplace/marketplace.selectors.ts index 7926f84d99..ebaa6f54ba 100644 --- a/apps/chat/src/store/marketplace/marketplace.selectors.ts +++ b/apps/chat/src/store/marketplace/marketplace.selectors.ts @@ -10,7 +10,12 @@ export const selectSelectedFilters = createSelector( (state) => state.selectedFilters, ); -export const selectSearchQuery = createSelector( +export const selectSearchTerm = createSelector( [rootSelector], - (state) => state.searchQuery, + (state) => state.searchTerm, +); + +export const selectSelectedTab = createSelector( + [rootSelector], + (state) => state.selectedTab, ); From 54c9e37b492e4f69b8191a22300dac7b3d435deb Mon Sep 17 00:00:00 2001 From: Magomed-Elbi Dzhukalaev Date: Mon, 16 Sep 2024 18:48:26 +0300 Subject: [PATCH 03/13] feat(chat): create separate components for cards list & search header --- .../src/components/Marketplace/CardsList.tsx | 44 +++++++++++ .../components/Marketplace/Marketplace.tsx | 79 ++++++++----------- .../components/Marketplace/SearchHeader.tsx | 62 +++++++++++++++ 3 files changed, 141 insertions(+), 44 deletions(-) create mode 100644 apps/chat/src/components/Marketplace/CardsList.tsx create mode 100644 apps/chat/src/components/Marketplace/SearchHeader.tsx diff --git a/apps/chat/src/components/Marketplace/CardsList.tsx b/apps/chat/src/components/Marketplace/CardsList.tsx new file mode 100644 index 0000000000..13c8a5723f --- /dev/null +++ b/apps/chat/src/components/Marketplace/CardsList.tsx @@ -0,0 +1,44 @@ +import { useTranslation } from 'next-i18next'; + +import { DialAIEntityModel } from '@/src/types/models'; +import { PublishActions } from '@/src/types/publication'; +import { Translation } from '@/src/types/translation'; + +import { ApplicationCard } from '@/src/components/Marketplace/ApplicationCard'; + +interface CardsListProps { + entities: DialAIEntityModel[]; + onCardClick: (entity: DialAIEntityModel) => void; + onPublish: (entity: DialAIEntityModel, action: PublishActions) => void; + isMobile?: boolean; + title?: string; + className?: string; +} + +export const CardsList = ({ + entities, + onCardClick, + onPublish, + isMobile, + title, + className, +}: CardsListProps) => { + const { t } = useTranslation(Translation.Marketplace); + + return ( +
+ {!!title &&

{t(title)}

} +
+ {entities.map((entity) => ( + + ))} +
+
+ ); +}; diff --git a/apps/chat/src/components/Marketplace/Marketplace.tsx b/apps/chat/src/components/Marketplace/Marketplace.tsx index 701bf2783d..58fb578d8f 100644 --- a/apps/chat/src/components/Marketplace/Marketplace.tsx +++ b/apps/chat/src/components/Marketplace/Marketplace.tsx @@ -1,8 +1,8 @@ import { FloatingOverlay } from '@floating-ui/react'; -import { IconSearch } from '@tabler/icons-react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'next-i18next'; +import { useSearchParams } from 'next/navigation'; import { groupModelsAndSaveOrder } from '@/src/utils/app/conversation'; import { getFolderIdFromEntityId } from '@/src/utils/app/folders'; @@ -17,18 +17,26 @@ import { SharingType } from '@/src/types/share'; import { Translation } from '@/src/types/translation'; import { useAppDispatch, useAppSelector } from '@/src/store/hooks'; -import { MarketplaceSelectors } from '@/src/store/marketplace/marketplace.reducers'; +import { + MarketplaceActions, + MarketplaceSelectors, +} from '@/src/store/marketplace/marketplace.reducers'; import { ModelsActions, ModelsSelectors, } from '@/src/store/models/models.reducers'; import { UISelectors } from '@/src/store/ui/ui.reducers'; -import { FilterTypes } from '@/src/constants/marketplace'; +import { + FilterTypes, + MarketplaceQueryParams, + MarketplaceTabs, +} from '@/src/constants/marketplace'; import { PublishModal } from '@/src/components/Chat/Publish/PublishWizard'; import { Spinner } from '@/src/components/Common/Spinner'; -import { ApplicationCard } from '@/src/components/Marketplace/ApplicationCard'; +import { CardsList } from '@/src/components/Marketplace/CardsList'; +import { SearchHeader } from '@/src/components/Marketplace/SearchHeader'; import ApplicationDetails from './ApplicationDetails/ApplicationDetails'; @@ -36,6 +44,7 @@ const Marketplace = () => { const { t } = useTranslation(Translation.Marketplace); const dispatch = useAppDispatch(); + const searchParams = useSearchParams(); const isFilterbarOpen = useAppSelector( UISelectors.selectShowMarketplaceFilterbar, @@ -44,13 +53,12 @@ const Marketplace = () => { const isModelsLoading = useAppSelector(ModelsSelectors.selectModelsIsLoading); const models = useAppSelector(ModelsSelectors.selectModels); - const searchQuery = useAppSelector(MarketplaceSelectors.selectSearchQuery); + const searchTerm = useAppSelector(MarketplaceSelectors.selectSearchTerm); const selectedFilters = useAppSelector( MarketplaceSelectors.selectSelectedFilters, ); const [detailsModel, setDetailsModel] = useState(); - const [searchTerm, setSearchTerm] = useState(searchQuery); const [publishModel, setPublishModel] = useState<{ entity: ShareEntity; action: PublishActions; @@ -86,6 +94,15 @@ const Marketplace = () => { useEffect(() => { dispatch(ModelsActions.getModels()); }, [dispatch]); + useEffect(() => { + dispatch( + MarketplaceActions.setSelectedTab( + searchParams.get(MarketplaceQueryParams.fromConversation) + ? MarketplaceTabs.MY_APPLICATIONS + : MarketplaceTabs.HOME, + ), + ); + }, [dispatch, searchParams]); const displayedEntities = useMemo(() => { const filteredEntities = models.filter( @@ -111,7 +128,7 @@ const Marketplace = () => { ) : ( <> -
+

{t('Welcome to DIAL Marketplace')} @@ -122,44 +139,18 @@ const Marketplace = () => { )}

-
-
- {t('Home page: {{count}} applications', { - count: displayedEntities.length, - nsSeparator: '::', - })} -
-
- - setSearchTerm(e.target.value)} - className="w-[560px] rounded border-[1px] border-primary bg-transparent py-[11px] pl-[38px] pr-3 leading-4 outline-none placeholder:text-secondary focus-visible:border-accent-primary" - /> -
-
+
-
-

{t('All applications')}

-
- {displayedEntities.map((model) => ( - - ))} - {showOverlay && } -
-
+ + + + {showOverlay && } {detailsModel && ( { + const { t } = useTranslation(Translation.Marketplace); + + const dispatch = useAppDispatch(); + + const searchTerm = useAppSelector(MarketplaceSelectors.selectSearchTerm); + const selectedTab = useAppSelector(MarketplaceSelectors.selectSelectedTab); + + const onSearchChange = (e: ChangeEvent) => { + dispatch(MarketplaceActions.setSearchTerm(e.target.value)); + }; + + return ( +
+
+ {t('{{label}}: {{count}} applications', { + count: items, + label: countLabel[selectedTab], + nsSeparator: '::', + })} +
+
+ + +
+
+ ); +}; From 5b4eeaaf17b64cd0ab91465a95c148f2fad5209b Mon Sep 17 00:00:00 2001 From: Magomed-Elbi Dzhukalaev Date: Mon, 16 Sep 2024 19:01:58 +0300 Subject: [PATCH 04/13] feat(chat): move marketplace banner into separate component --- .../components/Marketplace/Marketplace.tsx | 41 +------------ .../Marketplace/MarketplaceBanner.tsx | 60 +++++++++++++++++++ 2 files changed, 62 insertions(+), 39 deletions(-) create mode 100644 apps/chat/src/components/Marketplace/MarketplaceBanner.tsx diff --git a/apps/chat/src/components/Marketplace/Marketplace.tsx b/apps/chat/src/components/Marketplace/Marketplace.tsx index e93e1be054..fe4419d83d 100644 --- a/apps/chat/src/components/Marketplace/Marketplace.tsx +++ b/apps/chat/src/components/Marketplace/Marketplace.tsx @@ -1,8 +1,6 @@ import { FloatingOverlay } from '@floating-ui/react'; -import { IconSearch } from '@tabler/icons-react'; import { useCallback, useEffect, useMemo, useState } from 'react'; -import { useTranslation } from 'next-i18next'; import { useSearchParams } from 'next/navigation'; import { groupModelsAndSaveOrder } from '@/src/utils/app/conversation'; @@ -15,7 +13,6 @@ import { ShareEntity } from '@/src/types/common'; import { DialAIEntityModel } from '@/src/types/models'; import { PublishActions } from '@/src/types/publication'; import { SharingType } from '@/src/types/share'; -import { Translation } from '@/src/types/translation'; import { useAppDispatch, useAppSelector } from '@/src/store/hooks'; import { @@ -37,31 +34,12 @@ import { import { PublishModal } from '@/src/components/Chat/Publish/PublishWizard'; import { Spinner } from '@/src/components/Common/Spinner'; import { CardsList } from '@/src/components/Marketplace/CardsList'; +import { MarketplaceBanner } from '@/src/components/Marketplace/MarketplaceBanner'; import { SearchHeader } from '@/src/components/Marketplace/SearchHeader'; import ApplicationDetails from './ApplicationDetails/ApplicationDetails'; -import darkMyAppsBanner from '@/public/images/banners/welcome-dark-my-apps.jpg'; -import darkBanner from '@/public/images/banners/welcome-dark.jpg'; -import lightMyAppsBanner from '@/public/images/banners/welcome-light-my-apps.jpg'; -import lightBanner from '@/public/images/banners/welcome-light.jpg'; - -enum Tabs { - Marketplace = 'Marketplace', - MyApps = 'MyApps', -} - -const getBannerSrc = (theme: string, tab: Tabs) => { - if (theme === 'dark') { - return tab === Tabs.MyApps ? darkMyAppsBanner.src : darkBanner.src; - } - - return tab === Tabs.MyApps ? lightMyAppsBanner.src : lightBanner.src; -}; - const Marketplace = () => { - const { t } = useTranslation(Translation.Marketplace); - const dispatch = useAppDispatch(); const searchParams = useSearchParams(); @@ -76,7 +54,6 @@ const Marketplace = () => { const selectedFilters = useAppSelector( MarketplaceSelectors.selectSelectedFilters, ); - const selectedTheme = useAppSelector(UISelectors.selectThemeState); const [detailsModel, setDetailsModel] = useState(); const [publishModel, setPublishModel] = useState<{ @@ -149,21 +126,7 @@ const Marketplace = () => { ) : ( <>
-
-

- {t('Welcome to DIAL Marketplace')} -

-

- {t( - 'Explore our AI offerings with your data and see how the boost your productivity!', - )} -

-
+
diff --git a/apps/chat/src/components/Marketplace/MarketplaceBanner.tsx b/apps/chat/src/components/Marketplace/MarketplaceBanner.tsx new file mode 100644 index 0000000000..b057569f62 --- /dev/null +++ b/apps/chat/src/components/Marketplace/MarketplaceBanner.tsx @@ -0,0 +1,60 @@ +import { useTranslation } from 'next-i18next'; + +import { Translation } from '@/src/types/translation'; + +import { useAppSelector } from '@/src/store/hooks'; +import { MarketplaceSelectors } from '@/src/store/marketplace/marketplace.reducers'; +import { UISelectors } from '@/src/store/ui/ui.reducers'; + +import { MarketplaceTabs } from '@/src/constants/marketplace'; + +import darkMyAppsBanner from '@/public/images/banners/welcome-dark-my-apps.jpg'; +import darkBanner from '@/public/images/banners/welcome-dark.jpg'; +import lightMyAppsBanner from '@/public/images/banners/welcome-light-my-apps.jpg'; +import lightBanner from '@/public/images/banners/welcome-light.jpg'; + +const bannerText = { + title: { + [MarketplaceTabs.HOME]: 'Welcome to DIAL Marketplace', + [MarketplaceTabs.MY_APPLICATIONS]: 'My Applications', + }, + subtitle: { + [MarketplaceTabs.HOME]: + 'Explore our AI offerings with your data and see how they boost your productivity!', + [MarketplaceTabs.MY_APPLICATIONS]: + 'Here you can manage the AI applications used in your daily work', + }, +}; + +const getBannerSrc = (theme: string, tab: MarketplaceTabs) => { + if (theme === 'dark') { + return tab === MarketplaceTabs.MY_APPLICATIONS + ? darkMyAppsBanner.src + : darkBanner.src; + } + + return tab === MarketplaceTabs.MY_APPLICATIONS + ? lightMyAppsBanner.src + : lightBanner.src; +}; + +export const MarketplaceBanner = () => { + const { t } = useTranslation(Translation.Marketplace); + + const selectedTheme = useAppSelector(UISelectors.selectThemeState); + const selectedTab = useAppSelector(MarketplaceSelectors.selectSelectedTab); + + return ( +
+

+ {t(bannerText.title[selectedTab])} +

+

{t(bannerText.subtitle[selectedTab])}

+
+ ); +}; From a9dc05ac698284f7fbf4535e7c5eb64d47056702 Mon Sep 17 00:00:00 2001 From: Magomed-Elbi Dzhukalaev Date: Mon, 16 Sep 2024 19:14:58 +0300 Subject: [PATCH 05/13] feat(chat): add tab selectors in sidebar --- .../Marketplace/MarketplaceFilterbar.tsx | 27 +++++++++++++++---- .../components/Marketplace/SearchHeader.tsx | 2 +- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/apps/chat/src/components/Marketplace/MarketplaceFilterbar.tsx b/apps/chat/src/components/Marketplace/MarketplaceFilterbar.tsx index 57ea25e777..f40ee1d343 100644 --- a/apps/chat/src/components/Marketplace/MarketplaceFilterbar.tsx +++ b/apps/chat/src/components/Marketplace/MarketplaceFilterbar.tsx @@ -3,6 +3,7 @@ import { IconCheck, IconChevronUp, IconHome, + IconLayoutGrid, TablerIconsProps, } from '@tabler/icons-react'; import { JSX, useState } from 'react'; @@ -22,7 +23,7 @@ import { } from '@/src/store/marketplace/marketplace.reducers'; import { UISelectors } from '@/src/store/ui/ui.reducers'; -import { FilterTypes } from '@/src/constants/marketplace'; +import { FilterTypes, MarketplaceTabs } from '@/src/constants/marketplace'; import { capitalize } from 'lodash'; @@ -69,6 +70,7 @@ interface ActionButtonProps { onClick: () => void; caption: string; Icon: (props: TablerIconsProps) => JSX.Element; + selected?: boolean; } const ActionButton = ({ @@ -76,12 +78,18 @@ const ActionButton = ({ onClick, caption, Icon, + selected, }: ActionButtonProps) => { return (
{showFilterbar && ( diff --git a/apps/chat/src/components/Marketplace/SearchHeader.tsx b/apps/chat/src/components/Marketplace/SearchHeader.tsx index 933b13e697..8e61b7a620 100644 --- a/apps/chat/src/components/Marketplace/SearchHeader.tsx +++ b/apps/chat/src/components/Marketplace/SearchHeader.tsx @@ -15,7 +15,7 @@ import { MarketplaceTabs } from '@/src/constants/marketplace'; const countLabel = { [MarketplaceTabs.HOME]: 'Home page', - [MarketplaceTabs.MY_APPLICATIONS]: 'My Applications', + [MarketplaceTabs.MY_APPLICATIONS]: 'My applications', }; interface SearchHeaderProps { From 6d867360688e9145b9f189d391a187ff2f3a4b07 Mon Sep 17 00:00:00 2001 From: Magomed-Elbi Dzhukalaev Date: Tue, 17 Sep 2024 14:25:00 +0300 Subject: [PATCH 06/13] feat(chat): add tab renderer, add delete card from installed --- .../Marketplace/ApplicationCard.tsx | 18 +++++- .../src/components/Marketplace/CardsList.tsx | 4 ++ .../components/Marketplace/Marketplace.tsx | 41 +++++++++--- .../Marketplace/MarketplaceBanner.tsx | 2 +- .../components/Marketplace/SearchHeader.tsx | 6 +- .../components/Marketplace/TabRenderer.tsx | 62 +++++++++++++++++++ 6 files changed, 116 insertions(+), 17 deletions(-) create mode 100644 apps/chat/src/components/Marketplace/TabRenderer.tsx diff --git a/apps/chat/src/components/Marketplace/ApplicationCard.tsx b/apps/chat/src/components/Marketplace/ApplicationCard.tsx index 9f2da67d20..b200ea912d 100644 --- a/apps/chat/src/components/Marketplace/ApplicationCard.tsx +++ b/apps/chat/src/components/Marketplace/ApplicationCard.tsx @@ -19,6 +19,11 @@ import { DialAIEntityModel } from '@/src/types/models'; import { PublishActions } from '@/src/types/publication'; import { Translation } from '@/src/types/translation'; +import { useAppSelector } from '@/src/store/hooks'; +import { MarketplaceSelectors } from '@/src/store/marketplace/marketplace.reducers'; + +import { MarketplaceTabs } from '@/src/constants/marketplace'; + import { ModelIcon } from '@/src/components/Chatbar/ModelIcon'; import ContextMenu from '@/src/components/Common/ContextMenu'; import { ApplicationTag } from '@/src/components/Marketplace/ApplicationTag'; @@ -50,6 +55,7 @@ interface ApplicationCardProps { entity: DialAIEntityModel; onClick: (entity: DialAIEntityModel) => void; onPublish: (entity: DialAIEntityModel, action: PublishActions) => void; + onDelete: (entity: DialAIEntityModel) => void; isMobile?: boolean; selected?: boolean; } @@ -57,12 +63,15 @@ interface ApplicationCardProps { export const ApplicationCard = ({ entity, onClick, + onDelete, isMobile, selected, onPublish, }: ApplicationCardProps) => { const { t } = useTranslation(Translation.Marketplace); + const selectedTab = useAppSelector(MarketplaceSelectors.selectSelectedTab); + const isPublishedEntity = isItemPublic(entity.id); const menuItems: DisplayMenuItemProps[] = useMemo( @@ -90,14 +99,17 @@ export const ApplicationCard = ({ { name: t('Delete'), dataQa: 'delete', - display: !isPublishedEntity, + display: selectedTab === MarketplaceTabs.MY_APPLICATIONS, Icon: (props: TablerIconsProps) => ( ), - onClick: (e: React.MouseEvent) => e.stopPropagation(), // placeholder + onClick: (e: React.MouseEvent) => { + e.stopPropagation(); + onDelete(entity); + }, }, ], - [entity, isPublishedEntity, onPublish, t], + [entity, isPublishedEntity, onPublish, t, selectedTab, onDelete], ); const iconSize = diff --git a/apps/chat/src/components/Marketplace/CardsList.tsx b/apps/chat/src/components/Marketplace/CardsList.tsx index 13c8a5723f..9a24674d46 100644 --- a/apps/chat/src/components/Marketplace/CardsList.tsx +++ b/apps/chat/src/components/Marketplace/CardsList.tsx @@ -10,6 +10,7 @@ interface CardsListProps { entities: DialAIEntityModel[]; onCardClick: (entity: DialAIEntityModel) => void; onPublish: (entity: DialAIEntityModel, action: PublishActions) => void; + onDelete: (entity: DialAIEntityModel) => void; isMobile?: boolean; title?: string; className?: string; @@ -19,6 +20,7 @@ export const CardsList = ({ entities, onCardClick, onPublish, + onDelete, isMobile, title, className, @@ -28,6 +30,7 @@ export const CardsList = ({ return (
{!!title &&

{t(title)}

} +
{entities.map((entity) => ( ))} diff --git a/apps/chat/src/components/Marketplace/Marketplace.tsx b/apps/chat/src/components/Marketplace/Marketplace.tsx index fe4419d83d..25d0ab3c85 100644 --- a/apps/chat/src/components/Marketplace/Marketplace.tsx +++ b/apps/chat/src/components/Marketplace/Marketplace.tsx @@ -32,10 +32,9 @@ import { } from '@/src/constants/marketplace'; import { PublishModal } from '@/src/components/Chat/Publish/PublishWizard'; +import { ConfirmDialog } from '@/src/components/Common/ConfirmDialog'; import { Spinner } from '@/src/components/Common/Spinner'; -import { CardsList } from '@/src/components/Marketplace/CardsList'; -import { MarketplaceBanner } from '@/src/components/Marketplace/MarketplaceBanner'; -import { SearchHeader } from '@/src/components/Marketplace/SearchHeader'; +import { TabRenderer } from '@/src/components/Marketplace/TabRenderer'; import ApplicationDetails from './ApplicationDetails/ApplicationDetails'; @@ -50,6 +49,7 @@ const Marketplace = () => { const isModelsLoading = useAppSelector(ModelsSelectors.selectModelsIsLoading); const models = useAppSelector(ModelsSelectors.selectModels); + const installedModels = useAppSelector(ModelsSelectors.selectInstalledModels); const searchTerm = useAppSelector(MarketplaceSelectors.selectSearchTerm); const selectedFilters = useAppSelector( MarketplaceSelectors.selectSelectedFilters, @@ -60,6 +60,7 @@ const Marketplace = () => { entity: ShareEntity; action: PublishActions; }>(); + const [deleteModel, setDeleteModel] = useState(); const [isMobile, setIsMobile] = useState(isSmallScreen()); const handleSetPublishEntity = useCallback( @@ -75,6 +76,21 @@ const Marketplace = () => { [], ); + const handleDeleteClose = useCallback( + (confirm: boolean) => { + if (confirm && deleteModel) { + const filteredModels = installedModels.filter( + (model) => deleteModel.id !== model.id, + ); + + dispatch(ModelsActions.updateInstalledModels(filteredModels)); + } + + setDeleteModel(undefined); + }, + [deleteModel, dispatch, installedModels], + ); + const handlePublishClose = useCallback(() => setPublishModel(undefined), []); const showOverlay = (isFilterbarOpen || isProfileOpen) && isSmallScreen(); @@ -125,17 +141,12 @@ const Marketplace = () => {
) : ( <> -
- - -
- - {showOverlay && } @@ -159,6 +170,16 @@ const Marketplace = () => { publishAction={publishModel.action} /> )} + {!!deleteModel && ( + + )} ); }; diff --git a/apps/chat/src/components/Marketplace/MarketplaceBanner.tsx b/apps/chat/src/components/Marketplace/MarketplaceBanner.tsx index b057569f62..f55e455a48 100644 --- a/apps/chat/src/components/Marketplace/MarketplaceBanner.tsx +++ b/apps/chat/src/components/Marketplace/MarketplaceBanner.tsx @@ -46,7 +46,7 @@ export const MarketplaceBanner = () => { return (
{ return (
-
+
{t('{{label}}: {{count}} applications', { count: items, label: countLabel[selectedTab], nsSeparator: '::', })}
-
+
{ type="text" value={searchTerm} onChange={onSearchChange} - className="w-[560px] rounded border-[1px] border-primary bg-transparent py-[11px] pl-[38px] pr-3 leading-4 outline-none placeholder:text-secondary focus-visible:border-accent-primary" + className="w-full rounded border-[1px] border-primary bg-transparent py-[11px] pl-[38px] pr-3 leading-4 outline-none placeholder:text-secondary focus-visible:border-accent-primary" />
diff --git a/apps/chat/src/components/Marketplace/TabRenderer.tsx b/apps/chat/src/components/Marketplace/TabRenderer.tsx new file mode 100644 index 0000000000..85adcf6e00 --- /dev/null +++ b/apps/chat/src/components/Marketplace/TabRenderer.tsx @@ -0,0 +1,62 @@ +import { useMemo } from 'react'; + +import { DialAIEntityModel } from '@/src/types/models'; +import { PublishActions } from '@/src/types/publication'; + +import { useAppSelector } from '@/src/store/hooks'; +import { MarketplaceSelectors } from '@/src/store/marketplace/marketplace.reducers'; +import { ModelsSelectors } from '@/src/store/models/models.reducers'; + +import { MarketplaceTabs } from '@/src/constants/marketplace'; + +import { CardsList } from '@/src/components/Marketplace/CardsList'; +import { MarketplaceBanner } from '@/src/components/Marketplace/MarketplaceBanner'; +import { SearchHeader } from '@/src/components/Marketplace/SearchHeader'; + +interface TabRendererProps { + entities: DialAIEntityModel[]; + onCardClick: (entity: DialAIEntityModel) => void; + onPublish: (entity: DialAIEntityModel, action: PublishActions) => void; + onDelete: (entity: DialAIEntityModel) => void; + isMobile?: boolean; +} + +export const TabRenderer = ({ + entities, + onCardClick, + onPublish, + onDelete, + isMobile, +}: TabRendererProps) => { + const installedModels = useAppSelector(ModelsSelectors.selectInstalledModels); + const selectedTab = useAppSelector(MarketplaceSelectors.selectSelectedTab); + + const filteredModels = useMemo(() => { + if (selectedTab === MarketplaceTabs.MY_APPLICATIONS) { + return entities.filter( + (entity) => !!installedModels.find((model) => model.id === entity.id), + ); + } + return entities; + }, [selectedTab, entities, installedModels]); + + return ( + <> +
+ + +
+ + + + ); +}; From 4be775aa9e65e1a4d60472d0d82808bcd0323b37 Mon Sep 17 00:00:00 2001 From: Magomed-Elbi Dzhukalaev Date: Tue, 17 Sep 2024 14:42:45 +0300 Subject: [PATCH 07/13] feat(chat): show modals only after loading --- .../components/Marketplace/Marketplace.tsx | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/apps/chat/src/components/Marketplace/Marketplace.tsx b/apps/chat/src/components/Marketplace/Marketplace.tsx index 25d0ab3c85..43a2f865f6 100644 --- a/apps/chat/src/components/Marketplace/Marketplace.tsx +++ b/apps/chat/src/components/Marketplace/Marketplace.tsx @@ -150,6 +150,7 @@ const Marketplace = () => { /> {showOverlay && } + {detailsModel && ( { onClose={() => setDetailsModel(undefined)} /> )} - - )} - {!!(publishModel && publishModel?.entity?.id) && ( - - )} - {!!deleteModel && ( - + {!!(publishModel && publishModel?.entity?.id) && ( + + )} + + {!!deleteModel && ( + + )} + )}
); From 61c4d8aec50ee0f85eb2a41d7a1997be9bef7757 Mon Sep 17 00:00:00 2001 From: Magomed-Elbi Dzhukalaev Date: Tue, 17 Sep 2024 15:15:59 +0300 Subject: [PATCH 08/13] fix(chat): move marketplace link above new chat btn --- apps/chat/src/components/Chatbar/Chatbar.tsx | 42 +++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/apps/chat/src/components/Chatbar/Chatbar.tsx b/apps/chat/src/components/Chatbar/Chatbar.tsx index 4f6db0e54b..fc3b1955fe 100644 --- a/apps/chat/src/components/Chatbar/Chatbar.tsx +++ b/apps/chat/src/components/Chatbar/Chatbar.tsx @@ -1,38 +1,52 @@ import { IconApps } from '@tabler/icons-react'; import { DragEvent, useCallback } from 'react'; + + import { useTranslation } from 'next-i18next'; import { useRouter } from 'next/router'; + + import { isEntityNameOnSameLevelUnique } from '@/src/utils/app/common'; import { getConversationRootId } from '@/src/utils/app/id'; import { MoveType } from '@/src/utils/app/move'; + + import { ConversationInfo } from '@/src/types/chat'; import { FeatureType } from '@/src/types/common'; import { SearchFilters } from '@/src/types/search'; import { Translation } from '@/src/types/translation'; -import { - ConversationsActions, - ConversationsSelectors, -} from '@/src/store/conversations/conversations.reducers'; + + +import { ConversationsActions, ConversationsSelectors } from '@/src/store/conversations/conversations.reducers'; import { useAppDispatch, useAppSelector } from '@/src/store/hooks'; import { SettingsSelectors } from '@/src/store/settings/settings.reducers'; import { UIActions, UISelectors } from '@/src/store/ui/ui.reducers'; + + import { DEFAULT_CONVERSATION_NAME } from '@/src/constants/default-ui-settings'; + + import { Spinner } from '@/src/components/Common/Spinner'; + + import PlusIcon from '../../../public/images/icons/plus-large.svg'; import Sidebar from '../Sidebar'; import { ChatFolders } from './ChatFolders'; import { ChatbarSettings } from './ChatbarSettings'; import { Conversations } from './Conversations'; + + import { Feature } from '@epam/ai-dial-shared'; + const ChatActionsBlock = () => { const router = useRouter(); const { t } = useTranslation(Translation.SideBar); @@ -53,6 +67,16 @@ const ChatActionsBlock = () => { return ( <> +
+ +
-
- -
); }; From 3cad4c577dc5ce56e98d50995cfbc19c182944bc Mon Sep 17 00:00:00 2001 From: Magomed-Elbi Dzhukalaev Date: Tue, 17 Sep 2024 16:12:46 +0300 Subject: [PATCH 09/13] feat(chat): add new application btn --- apps/chat/src/components/Chatbar/Chatbar.tsx | 22 ++------- .../components/Marketplace/Marketplace.tsx | 1 + .../components/Marketplace/SearchHeader.tsx | 45 ++++++++++++------- .../components/Marketplace/TabRenderer.tsx | 17 ++++++- 4 files changed, 50 insertions(+), 35 deletions(-) diff --git a/apps/chat/src/components/Chatbar/Chatbar.tsx b/apps/chat/src/components/Chatbar/Chatbar.tsx index fc3b1955fe..75a79b390b 100644 --- a/apps/chat/src/components/Chatbar/Chatbar.tsx +++ b/apps/chat/src/components/Chatbar/Chatbar.tsx @@ -1,52 +1,38 @@ import { IconApps } from '@tabler/icons-react'; import { DragEvent, useCallback } from 'react'; - - import { useTranslation } from 'next-i18next'; import { useRouter } from 'next/router'; - - import { isEntityNameOnSameLevelUnique } from '@/src/utils/app/common'; import { getConversationRootId } from '@/src/utils/app/id'; import { MoveType } from '@/src/utils/app/move'; - - import { ConversationInfo } from '@/src/types/chat'; import { FeatureType } from '@/src/types/common'; import { SearchFilters } from '@/src/types/search'; import { Translation } from '@/src/types/translation'; - - -import { ConversationsActions, ConversationsSelectors } from '@/src/store/conversations/conversations.reducers'; +import { + ConversationsActions, + ConversationsSelectors, +} from '@/src/store/conversations/conversations.reducers'; import { useAppDispatch, useAppSelector } from '@/src/store/hooks'; import { SettingsSelectors } from '@/src/store/settings/settings.reducers'; import { UIActions, UISelectors } from '@/src/store/ui/ui.reducers'; - - import { DEFAULT_CONVERSATION_NAME } from '@/src/constants/default-ui-settings'; - - import { Spinner } from '@/src/components/Common/Spinner'; - - import PlusIcon from '../../../public/images/icons/plus-large.svg'; import Sidebar from '../Sidebar'; import { ChatFolders } from './ChatFolders'; import { ChatbarSettings } from './ChatbarSettings'; import { Conversations } from './Conversations'; - - import { Feature } from '@epam/ai-dial-shared'; - const ChatActionsBlock = () => { const router = useRouter(); const { t } = useTranslation(Translation.SideBar); diff --git a/apps/chat/src/components/Marketplace/Marketplace.tsx b/apps/chat/src/components/Marketplace/Marketplace.tsx index 43a2f865f6..a36ffcdd5e 100644 --- a/apps/chat/src/components/Marketplace/Marketplace.tsx +++ b/apps/chat/src/components/Marketplace/Marketplace.tsx @@ -61,6 +61,7 @@ const Marketplace = () => { action: PublishActions; }>(); const [deleteModel, setDeleteModel] = useState(); + const [isMobile, setIsMobile] = useState(isSmallScreen()); const handleSetPublishEntity = useCallback( diff --git a/apps/chat/src/components/Marketplace/SearchHeader.tsx b/apps/chat/src/components/Marketplace/SearchHeader.tsx index 7831811f59..85d12247e5 100644 --- a/apps/chat/src/components/Marketplace/SearchHeader.tsx +++ b/apps/chat/src/components/Marketplace/SearchHeader.tsx @@ -1,4 +1,4 @@ -import { IconSearch } from '@tabler/icons-react'; +import { IconPlus, IconSearch } from '@tabler/icons-react'; import { ChangeEvent } from 'react'; import { useTranslation } from 'next-i18next'; @@ -20,9 +20,13 @@ const countLabel = { interface SearchHeaderProps { items: number; + onAddApplication: () => void; } -export const SearchHeader = ({ items }: SearchHeaderProps) => { +export const SearchHeader = ({ + items, + onAddApplication, +}: SearchHeaderProps) => { const { t } = useTranslation(Translation.Marketplace); const dispatch = useAppDispatch(); @@ -43,19 +47,30 @@ export const SearchHeader = ({ items }: SearchHeaderProps) => { nsSeparator: '::', })}
-
- - +
+
+ + +
+ {selectedTab === MarketplaceTabs.MY_APPLICATIONS && ( + + )}
); diff --git a/apps/chat/src/components/Marketplace/TabRenderer.tsx b/apps/chat/src/components/Marketplace/TabRenderer.tsx index 85adcf6e00..d306ac36e9 100644 --- a/apps/chat/src/components/Marketplace/TabRenderer.tsx +++ b/apps/chat/src/components/Marketplace/TabRenderer.tsx @@ -1,4 +1,4 @@ -import { useMemo } from 'react'; +import { useMemo, useState } from 'react'; import { DialAIEntityModel } from '@/src/types/models'; import { PublishActions } from '@/src/types/publication'; @@ -9,6 +9,7 @@ import { ModelsSelectors } from '@/src/store/models/models.reducers'; import { MarketplaceTabs } from '@/src/constants/marketplace'; +import { ApplicationDialog } from '@/src/components/Common/ApplicationDialog'; import { CardsList } from '@/src/components/Marketplace/CardsList'; import { MarketplaceBanner } from '@/src/components/Marketplace/MarketplaceBanner'; import { SearchHeader } from '@/src/components/Marketplace/SearchHeader'; @@ -31,6 +32,8 @@ export const TabRenderer = ({ const installedModels = useAppSelector(ModelsSelectors.selectInstalledModels); const selectedTab = useAppSelector(MarketplaceSelectors.selectSelectedTab); + const [addModal, setAddModal] = useState(false); + const filteredModels = useMemo(() => { if (selectedTab === MarketplaceTabs.MY_APPLICATIONS) { return entities.filter( @@ -44,7 +47,10 @@ export const TabRenderer = ({ <>
- + setAddModal(true)} + />
+ + {addModal && ( + setAddModal(false)} + /> + )} ); }; From 0640468dc0af7bc89bd42a4ce742117befb198a7 Mon Sep 17 00:00:00 2001 From: Magomed-Elbi Dzhukalaev Date: Tue, 17 Sep 2024 18:21:56 +0300 Subject: [PATCH 10/13] feat(chat): add remove/add/delete/edit app actions --- .../components/Common/FolderContextMenu.tsx | 10 +- .../src/components/Common/ItemContextMenu.tsx | 12 +- .../Marketplace/ApplicationCard.tsx | 68 +++++++++-- .../ApplicationDetails/ApplicationFooter.tsx | 19 +++- .../src/components/Marketplace/CardsList.tsx | 10 +- .../components/Marketplace/Marketplace.tsx | 33 +----- .../components/Marketplace/TabRenderer.tsx | 106 ++++++++++++++++-- apps/chat/src/types/applications.ts | 5 + apps/chat/src/utils/app/publications.ts | 3 - 9 files changed, 198 insertions(+), 68 deletions(-) diff --git a/apps/chat/src/components/Common/FolderContextMenu.tsx b/apps/chat/src/components/Common/FolderContextMenu.tsx index bdf66543d5..50399c33e9 100644 --- a/apps/chat/src/components/Common/FolderContextMenu.tsx +++ b/apps/chat/src/components/Common/FolderContextMenu.tsx @@ -19,7 +19,6 @@ import { isEntityNameInvalid, } from '@/src/utils/app/common'; import { getRootId } from '@/src/utils/app/id'; -import { isItemPublic } from '@/src/utils/app/publications'; import { isEntityOrParentsExternal } from '@/src/utils/app/share'; import { FeatureType } from '@/src/types/common'; @@ -30,6 +29,8 @@ import { Translation } from '@/src/types/translation'; import { useAppSelector } from '@/src/store/hooks'; import { SettingsSelectors } from '@/src/store/settings/settings.reducers'; +import { PUBLIC_URL_PREFIX } from '@/src/constants/public'; + import ContextMenu from './ContextMenu'; import UnpublishIcon from '@/public/images/icons/unpublish.svg'; @@ -159,7 +160,12 @@ export const FolderContextMenu = ({ dataQa: 'unpublish', display: isPublishingEnabled && - isItemPublic(folder.id) && + folder.id.startsWith( + getRootId({ + featureType, + bucket: PUBLIC_URL_PREFIX, + }), + ) && !!onUnpublish && isSidePanelFolder, Icon: UnpublishIcon, diff --git a/apps/chat/src/components/Common/ItemContextMenu.tsx b/apps/chat/src/components/Common/ItemContextMenu.tsx index 16ccef5c50..d1375af7fc 100644 --- a/apps/chat/src/components/Common/ItemContextMenu.tsx +++ b/apps/chat/src/components/Common/ItemContextMenu.tsx @@ -26,7 +26,6 @@ import { isEntityNameInvalid, } from '@/src/utils/app/common'; import { getRootId } from '@/src/utils/app/id'; -import { isItemPublic } from '@/src/utils/app/publications'; import { isEntityOrParentsExternal } from '@/src/utils/app/share'; import { FeatureType, ShareEntity } from '@/src/types/common'; @@ -37,6 +36,8 @@ import { Translation } from '@/src/types/translation'; import { useAppSelector } from '@/src/store/hooks'; import { SettingsSelectors } from '@/src/store/settings/settings.reducers'; +import { PUBLIC_URL_PREFIX } from '@/src/constants/public'; + import ContextMenu from './ContextMenu'; import UnpublishIcon from '@/public/images/icons/unpublish.svg'; @@ -293,7 +294,14 @@ export default function ItemContextMenu({ name: t('Unpublish'), dataQa: 'unpublish', display: - isPublishingEnabled && !!onUnpublish && isItemPublic(entity.id), + isPublishingEnabled && + !!onUnpublish && + entity.id.startsWith( + getRootId({ + featureType, + bucket: PUBLIC_URL_PREFIX, + }), + ), Icon: UnpublishIcon, onClick: onUnpublish, disabled: disableAll, diff --git a/apps/chat/src/components/Marketplace/ApplicationCard.tsx b/apps/chat/src/components/Marketplace/ApplicationCard.tsx index b200ea912d..a026a63ace 100644 --- a/apps/chat/src/components/Marketplace/ApplicationCard.tsx +++ b/apps/chat/src/components/Marketplace/ApplicationCard.tsx @@ -1,5 +1,6 @@ import { IconDotsVertical, + IconPencilMinus, IconTrashX, IconWorldShare, TablerIconsProps, @@ -10,8 +11,8 @@ import { useTranslation } from 'next-i18next'; import classnames from 'classnames'; +import { getRootId } from '@/src/utils/app/id'; import { isSmallScreen } from '@/src/utils/app/mobile'; -import { isItemPublic } from '@/src/utils/app/publications'; import { FeatureType } from '@/src/types/common'; import { DisplayMenuItemProps } from '@/src/types/menu'; @@ -23,6 +24,7 @@ import { useAppSelector } from '@/src/store/hooks'; import { MarketplaceSelectors } from '@/src/store/marketplace/marketplace.reducers'; import { MarketplaceTabs } from '@/src/constants/marketplace'; +import { PUBLIC_URL_PREFIX } from '@/src/constants/public'; import { ModelIcon } from '@/src/components/Chatbar/ModelIcon'; import ContextMenu from '@/src/components/Common/ContextMenu'; @@ -54,8 +56,10 @@ export const CardFooter = () => { interface ApplicationCardProps { entity: DialAIEntityModel; onClick: (entity: DialAIEntityModel) => void; - onPublish: (entity: DialAIEntityModel, action: PublishActions) => void; - onDelete: (entity: DialAIEntityModel) => void; + onPublish?: (entity: DialAIEntityModel, action: PublishActions) => void; + onDelete?: (entity: DialAIEntityModel) => void; + onEdit?: (entity: DialAIEntityModel) => void; + onRemove?: (entity: DialAIEntityModel) => void; isMobile?: boolean; selected?: boolean; } @@ -64,6 +68,8 @@ export const ApplicationCard = ({ entity, onClick, onDelete, + onEdit, + onRemove, isMobile, selected, onPublish, @@ -72,44 +78,84 @@ export const ApplicationCard = ({ const selectedTab = useAppSelector(MarketplaceSelectors.selectSelectedTab); - const isPublishedEntity = isItemPublic(entity.id); + const isPublishedEntity = entity.id.startsWith( + getRootId({ + featureType: FeatureType.Application, + bucket: PUBLIC_URL_PREFIX, + }), + ); + const isMyEntity = entity.id.startsWith( + getRootId({ featureType: FeatureType.Application }), + ); const menuItems: DisplayMenuItemProps[] = useMemo( () => [ + { + name: t('Edit'), + dataQa: 'edit', + display: isMyEntity && !!onEdit, + Icon: IconPencilMinus, + onClick: (e: React.MouseEvent) => { + e.stopPropagation(); + onEdit?.(entity); + }, + }, { name: t('Publish'), dataQa: 'publish', - display: !isPublishedEntity, + display: isMyEntity && !!onPublish, Icon: IconWorldShare, onClick: (e: React.MouseEvent) => { e.stopPropagation(); - onPublish(entity, PublishActions.ADD); + onPublish?.(entity, PublishActions.ADD); }, }, { name: t('Unpublish'), dataQa: 'unpublish', - display: isPublishedEntity, + display: isPublishedEntity && !!onPublish, Icon: UnpublishIcon, onClick: (e: React.MouseEvent) => { e.stopPropagation(); - onPublish(entity, PublishActions.DELETE); + onPublish?.(entity, PublishActions.DELETE); }, }, { name: t('Delete'), dataQa: 'delete', - display: selectedTab === MarketplaceTabs.MY_APPLICATIONS, + display: isMyEntity && !!onDelete, + Icon: (props: TablerIconsProps) => ( + + ), + onClick: (e: React.MouseEvent) => { + e.stopPropagation(); + onDelete?.(entity); + }, + }, + { + name: t('Remove'), + dataQa: 'remove', + display: selectedTab === MarketplaceTabs.MY_APPLICATIONS && !!onRemove, Icon: (props: TablerIconsProps) => ( ), onClick: (e: React.MouseEvent) => { e.stopPropagation(); - onDelete(entity); + onRemove?.(entity); }, }, ], - [entity, isPublishedEntity, onPublish, t, selectedTab, onDelete], + [ + entity, + isPublishedEntity, + onPublish, + t, + selectedTab, + onDelete, + isMyEntity, + onEdit, + onRemove, + ], ); const iconSize = diff --git a/apps/chat/src/components/Marketplace/ApplicationDetails/ApplicationFooter.tsx b/apps/chat/src/components/Marketplace/ApplicationDetails/ApplicationFooter.tsx index eefe2b5106..96a4056c1c 100644 --- a/apps/chat/src/components/Marketplace/ApplicationDetails/ApplicationFooter.tsx +++ b/apps/chat/src/components/Marketplace/ApplicationDetails/ApplicationFooter.tsx @@ -2,13 +2,15 @@ import { IconPlayerPlay, IconShare, IconWorldShare } from '@tabler/icons-react'; import { useTranslation } from 'next-i18next'; -import { isApplicationId } from '@/src/utils/app/id'; -import { isItemPublic } from '@/src/utils/app/publications'; +import { getRootId, isApplicationId } from '@/src/utils/app/id'; +import { FeatureType } from '@/src/types/common'; import { DialAIEntityModel } from '@/src/types/models'; import { PublishActions } from '@/src/types/publication'; import { Translation } from '@/src/types/translation'; +import { PUBLIC_URL_PREFIX } from '@/src/constants/public'; + import { ModelVersionSelect } from '../../Chat/ModelVersionSelect'; import Tooltip from '../../Common/Tooltip'; @@ -33,6 +35,13 @@ export const ApplicationDetailsFooter = ({ }: Props) => { const { t } = useTranslation(Translation.Marketplace); + const isPublishedApplication = entity.id.startsWith( + getRootId({ + featureType: FeatureType.Application, + bucket: PUBLIC_URL_PREFIX, + }), + ); + return (
@@ -43,13 +52,13 @@ export const ApplicationDetailsFooter = ({ /> {isApplicationId(entity.id) && (
diff --git a/apps/chat/src/components/Marketplace/TabRenderer.tsx b/apps/chat/src/components/Marketplace/TabRenderer.tsx index d306ac36e9..6c9bea2949 100644 --- a/apps/chat/src/components/Marketplace/TabRenderer.tsx +++ b/apps/chat/src/components/Marketplace/TabRenderer.tsx @@ -1,24 +1,48 @@ -import { useMemo, useState } from 'react'; +import { useCallback, useMemo, useState } from 'react'; +import { ApplicationActionType } from '@/src/types/applications'; import { DialAIEntityModel } from '@/src/types/models'; import { PublishActions } from '@/src/types/publication'; -import { useAppSelector } from '@/src/store/hooks'; +import { ApplicationActions } from '@/src/store/application/application.reducers'; +import { useAppDispatch, useAppSelector } from '@/src/store/hooks'; import { MarketplaceSelectors } from '@/src/store/marketplace/marketplace.reducers'; -import { ModelsSelectors } from '@/src/store/models/models.reducers'; +import { + ModelsActions, + ModelsSelectors, +} from '@/src/store/models/models.reducers'; import { MarketplaceTabs } from '@/src/constants/marketplace'; import { ApplicationDialog } from '@/src/components/Common/ApplicationDialog'; +import { ConfirmDialog } from '@/src/components/Common/ConfirmDialog'; import { CardsList } from '@/src/components/Marketplace/CardsList'; import { MarketplaceBanner } from '@/src/components/Marketplace/MarketplaceBanner'; import { SearchHeader } from '@/src/components/Marketplace/SearchHeader'; +enum DeleteType { + DELETE, + REMOVE, +} + +const deleteConfirmationText = { + [DeleteType.DELETE]: { + heading: 'Confirm deleting application', + description: 'Are you sure you want to delete the application?', + confirmLabel: 'Delete', + }, + [DeleteType.REMOVE]: { + heading: 'Confirm removing application', + description: + 'Are you sure you want to remove the application from your list?', + confirmLabel: 'Remove', + }, +}; + interface TabRendererProps { entities: DialAIEntityModel[]; onCardClick: (entity: DialAIEntityModel) => void; onPublish: (entity: DialAIEntityModel, action: PublishActions) => void; - onDelete: (entity: DialAIEntityModel) => void; isMobile?: boolean; } @@ -26,13 +50,56 @@ export const TabRenderer = ({ entities, onCardClick, onPublish, - onDelete, isMobile, }: TabRendererProps) => { + const dispatch = useAppDispatch(); + const installedModels = useAppSelector(ModelsSelectors.selectInstalledModels); const selectedTab = useAppSelector(MarketplaceSelectors.selectSelectedTab); - const [addModal, setAddModal] = useState(false); + const [applicationModel, setApplicationModel] = useState<{ + action: ApplicationActionType; + entity?: DialAIEntityModel; + }>(); + const [deleteModel, setDeleteModel] = useState<{ + action: DeleteType; + entity: DialAIEntityModel; + }>(); + + const handleAddApplication = useCallback(() => { + setApplicationModel({ + action: ApplicationActionType.ADD, + }); + }, []); + + const handleEditApplication = useCallback( + (entity: DialAIEntityModel) => { + dispatch(ApplicationActions.get(entity.id)); + setApplicationModel({ + entity, + action: ApplicationActionType.EDIT, + }); + }, + [dispatch], + ); + + const handleDeleteClose = useCallback( + (confirm: boolean) => { + if (confirm && deleteModel) { + if (deleteModel.action === DeleteType.REMOVE) { + const filteredModels = installedModels.filter( + (model) => deleteModel.entity.id !== model.id, + ); + dispatch(ModelsActions.updateInstalledModels(filteredModels)); + } + if (deleteModel.action === DeleteType.DELETE) { + dispatch(ApplicationActions.delete(deleteModel.entity)); + } + } + setDeleteModel(undefined); + }, + [deleteModel, installedModels, dispatch], + ); const filteredModels = useMemo(() => { if (selectedTab === MarketplaceTabs.MY_APPLICATIONS) { @@ -49,7 +116,7 @@ export const TabRenderer = ({ setAddModal(true)} + onAddApplication={handleAddApplication} />
@@ -60,14 +127,31 @@ export const TabRenderer = ({ entities={filteredModels} onCardClick={onCardClick} onPublish={onPublish} - onDelete={onDelete} + onDelete={(entity) => + setDeleteModel({ entity, action: DeleteType.DELETE }) + } + onRemove={(entity) => + setDeleteModel({ entity, action: DeleteType.REMOVE }) + } + onEdit={handleEditApplication} isMobile={isMobile} /> - {addModal && ( + {/* MODALS */} + {!!applicationModel && ( setAddModal(false)} + isOpen={!!applicationModel} + isEdit={applicationModel.action === ApplicationActionType.EDIT} + currentReference={applicationModel.entity?.reference} + onClose={() => setApplicationModel(undefined)} + /> + )} + {!!deleteModel && ( + )} diff --git a/apps/chat/src/types/applications.ts b/apps/chat/src/types/applications.ts index 3001bdc380..a88f6fec6a 100644 --- a/apps/chat/src/types/applications.ts +++ b/apps/chat/src/types/applications.ts @@ -10,3 +10,8 @@ export interface CustomApplicationModel completionUrl: string; version: string; } + +export enum ApplicationActionType { + ADD = 'ADD', + EDIT = 'EDIT', +} diff --git a/apps/chat/src/utils/app/publications.ts b/apps/chat/src/utils/app/publications.ts index fe2062a69c..2d70036fde 100644 --- a/apps/chat/src/utils/app/publications.ts +++ b/apps/chat/src/utils/app/publications.ts @@ -22,9 +22,6 @@ import { getFolderIdFromEntityId, splitEntityId } from './folders'; import { isRootId } from './id'; import { EnumMapper } from './mappers'; -export const isItemPublic = (id: string) => - id.split('/')[1] === PUBLIC_URL_PREFIX; - export const createTargetUrl = ( featureType: FeatureType, publicPath: string, From de75c0a4ba1d09a8e5791fdf4bdb4237dc9487df Mon Sep 17 00:00:00 2001 From: Magomed-Elbi Dzhukalaev Date: Wed, 18 Sep 2024 13:13:58 +0300 Subject: [PATCH 11/13] fix(chat): wrap useCallback --- .../Marketplace/MarketplaceFilterbar.tsx | 13 +++++---- .../components/Marketplace/TabRenderer.tsx | 29 ++++++++++++++----- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/apps/chat/src/components/Marketplace/MarketplaceFilterbar.tsx b/apps/chat/src/components/Marketplace/MarketplaceFilterbar.tsx index f40ee1d343..24d214a2ad 100644 --- a/apps/chat/src/components/Marketplace/MarketplaceFilterbar.tsx +++ b/apps/chat/src/components/Marketplace/MarketplaceFilterbar.tsx @@ -6,7 +6,7 @@ import { IconLayoutGrid, TablerIconsProps, } from '@tabler/icons-react'; -import { JSX, useState } from 'react'; +import { JSX, useCallback, useState } from 'react'; import { useTranslation } from 'next-i18next'; import { useRouter } from 'next/router'; @@ -87,7 +87,7 @@ const ActionButton = ({ className={classNames( 'flex min-h-9 shrink-0 grow cursor-pointer select-none items-center gap-3 rounded px-4 py-2 transition-colors duration-200 hover:bg-accent-primary-alpha hover:disabled:bg-transparent', { - '!bg-accent-primary-alpha': selected, + 'bg-accent-primary-alpha': selected, }, )} > @@ -126,9 +126,12 @@ const MarketplaceFilterbar = () => { ); }; - const onTabClick = (tab: MarketplaceTabs) => { - dispatch(MarketplaceActions.setSelectedTab(tab)); - }; + const onTabClick = useCallback( + (tab: MarketplaceTabs) => { + dispatch(MarketplaceActions.setSelectedTab(tab)); + }, + [dispatch], + ); return (