Skip to content

Commit 076c7b8

Browse files
GimirMagomed-Elbi DzhukalaevAlexander-Kezik
authored
feat(chat): add document_relative_url to application sharing/publishing flow (Issue #3054) (#3078)
Co-authored-by: Magomed-Elbi Dzhukalaev <[email protected]> Co-authored-by: Alexander <[email protected]>
1 parent 8b6f709 commit 076c7b8

File tree

9 files changed

+382
-102
lines changed

9 files changed

+382
-102
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
import { IconDownload } from '@tabler/icons-react';
2+
import { useEffect, useMemo } from 'react';
3+
4+
import classNames from 'classnames';
5+
6+
import { useTranslation } from '@/src/hooks/useTranslation';
7+
8+
import { getQuickAppDocumentUrl } from '@/src/utils/app/application';
9+
import { constructPath } from '@/src/utils/app/file';
10+
import { splitEntityId } from '@/src/utils/app/folders';
11+
import { isEntityIdExternal } from '@/src/utils/app/id';
12+
import { isEntityIdPublic } from '@/src/utils/app/publications';
13+
14+
import { PublishRequestDialAIEntityModel } from '@/src/types/models';
15+
import { Translation } from '@/src/types/translation';
16+
17+
import { ApplicationSelectors } from '@/src/store/application/application.reducers';
18+
import { FilesActions, FilesSelectors } from '@/src/store/files/files.reducers';
19+
import { useAppDispatch, useAppSelector } from '@/src/store/hooks';
20+
21+
import CollapsibleSection from '@/src/components/Common/CollapsibleSection';
22+
import { ErrorMessage } from '@/src/components/Common/ErrorMessage';
23+
import {
24+
ApplicationRow,
25+
FilesRow,
26+
} from '@/src/components/Common/ReplaceConfirmationModal/Components';
27+
import { Spinner } from '@/src/components/Common/Spinner';
28+
29+
import { PublishActions } from '@epam/ai-dial-shared';
30+
31+
interface ApplicationPublishItemsProps {
32+
entity: PublishRequestDialAIEntityModel;
33+
handleSelectItems: (ids: string[]) => void;
34+
publishAction: PublishActions;
35+
chosenItemsIds: string[];
36+
}
37+
38+
export const ApplicationPublishItems = ({
39+
entity,
40+
handleSelectItems,
41+
publishAction,
42+
chosenItemsIds,
43+
}: ApplicationPublishItemsProps) => {
44+
const { t } = useTranslation(Translation.Chat);
45+
const dispatch = useAppDispatch();
46+
47+
const applicationDetails = useAppSelector(
48+
ApplicationSelectors.selectApplicationDetail,
49+
);
50+
const isApplicationLoading = useAppSelector(
51+
ApplicationSelectors.selectIsApplicationLoading,
52+
);
53+
const areFilesLoading = useAppSelector(FilesSelectors.selectAreFilesLoading);
54+
const files = useAppSelector(FilesSelectors.selectFiles);
55+
56+
const quickAppDocumentUrl = getQuickAppDocumentUrl(applicationDetails);
57+
const quickAppDocument = useMemo(
58+
() => files.find((file) => file.id === quickAppDocumentUrl),
59+
[files, quickAppDocumentUrl],
60+
);
61+
62+
useEffect(() => {
63+
if (quickAppDocumentUrl) {
64+
const { apiKey, bucket, parentPath } = splitEntityId(quickAppDocumentUrl);
65+
dispatch(
66+
FilesActions.getFiles({
67+
id: constructPath(apiKey, bucket, parentPath),
68+
}),
69+
);
70+
}
71+
}, [dispatch, quickAppDocumentUrl]);
72+
73+
return (
74+
<>
75+
<CollapsibleSection
76+
togglerClassName="!text-sm !text-primary"
77+
name={t('Applications')}
78+
openByDefault
79+
dataQa="applications-to-send-request"
80+
className="!pl-0"
81+
>
82+
<ApplicationRow
83+
onSelect={handleSelectItems}
84+
itemComponentClassNames={classNames(
85+
'cursor-pointer',
86+
publishAction === PublishActions.DELETE && 'text-error',
87+
)}
88+
item={entity}
89+
level={0}
90+
isChosen={chosenItemsIds.some((id) => id === entity.id)}
91+
/>
92+
</CollapsibleSection>
93+
94+
{publishAction === PublishActions.ADD &&
95+
'iconUrl' in entity &&
96+
entity.iconUrl &&
97+
isEntityIdExternal({ id: entity.iconUrl }) && (
98+
<CollapsibleSection
99+
togglerClassName="!text-sm !text-primary"
100+
name={t('Files')}
101+
openByDefault
102+
dataQa="files-to-send-request"
103+
className="!pl-0"
104+
>
105+
<ErrorMessage
106+
type="warning"
107+
error={t(
108+
`The icon used for this application is in the "${isEntityIdPublic({ id: entity.iconUrl }) ? 'Organization' : 'Shared with me'}" section and cannot be published. Please replace the icon, otherwise the application will be published with the default one.`,
109+
)}
110+
/>
111+
</CollapsibleSection>
112+
)}
113+
114+
{(isApplicationLoading || areFilesLoading) && (
115+
<div className="pl-3">
116+
<Spinner size={20} dataQa="publication-items-spinner" />
117+
</div>
118+
)}
119+
120+
{!isApplicationLoading && quickAppDocument && (
121+
<CollapsibleSection
122+
name={t('Files')}
123+
openByDefault
124+
dataQa="files-to-send-request"
125+
>
126+
<div className="flex items-center gap-2">
127+
<FilesRow
128+
itemComponentClassNames={classNames(
129+
'w-full cursor-pointer truncate',
130+
publishAction === PublishActions.DELETE && 'text-error',
131+
)}
132+
item={quickAppDocument}
133+
level={0}
134+
onSelect={handleSelectItems}
135+
isChosen={chosenItemsIds.some((id) => id === quickAppDocument.id)}
136+
/>
137+
<a
138+
download={quickAppDocument.name}
139+
href={constructPath('api', quickAppDocument.id)}
140+
data-qa="download"
141+
>
142+
<IconDownload
143+
className="shrink-0 text-secondary hover:text-accent-primary"
144+
size={18}
145+
/>
146+
</a>
147+
</div>
148+
</CollapsibleSection>
149+
)}
150+
</>
151+
);
152+
};

apps/chat/src/components/Chat/Publish/PublicationItemsList.tsx

+8-48
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,8 @@ import { useTranslation } from '@/src/hooks/useTranslation';
1616
import { findLatestVersion, isVersionValid } from '@/src/utils/app/common';
1717
import { constructPath } from '@/src/utils/app/file';
1818
import { splitEntityId } from '@/src/utils/app/folders';
19-
import {
20-
getIdWithoutRootPathSegments,
21-
getRootId,
22-
isEntityIdExternal,
23-
} from '@/src/utils/app/id';
19+
import { getIdWithoutRootPathSegments, getRootId } from '@/src/utils/app/id';
2420
import { EnumMapper } from '@/src/utils/app/mappers';
25-
import { isEntityIdPublic } from '@/src/utils/app/publications';
2621

2722
import { Conversation } from '@/src/types/chat';
2823
import { FeatureType } from '@/src/types/common';
@@ -46,15 +41,14 @@ import {
4641
PUBLIC_URL_PREFIX,
4742
} from '@/src/constants/public';
4843

44+
import { ApplicationPublishItems } from '@/src/components/Chat/Publish/ApplicationPublishItems';
4945
import CollapsibleSection from '@/src/components/Common/CollapsibleSection';
5046
import {
51-
ApplicationRow,
5247
ConversationRow,
5348
FilesRow,
5449
PromptsRow,
5550
} from '@/src/components/Common/ReplaceConfirmationModal/Components';
5651

57-
import { ErrorMessage } from '../../Common/ErrorMessage';
5852
import Tooltip from '../../Common/Tooltip';
5953
import Folder from '../../Folder/Folder';
6054
import { PublicVersionSelector } from './PublicVersionSelector';
@@ -546,46 +540,12 @@ export const PublicationItemsList = memo(
546540
</CollapsibleSection>
547541
)}
548542
{type === SharingType.Application && (
549-
<>
550-
<CollapsibleSection
551-
togglerClassName="!text-sm !text-primary"
552-
name={t('Applications')}
553-
openByDefault
554-
dataQa="applications-to-send-request"
555-
className="!pl-0"
556-
>
557-
<ApplicationRow
558-
onSelect={handleSelectItems}
559-
itemComponentClassNames={classNames(
560-
'cursor-pointer',
561-
publishAction === PublishActions.DELETE && 'text-error',
562-
)}
563-
item={entity}
564-
level={0}
565-
isChosen={chosenItemsIds.some((id) => id === entity.id)}
566-
/>
567-
</CollapsibleSection>
568-
569-
{publishAction === PublishActions.ADD &&
570-
'iconUrl' in entity &&
571-
entity.iconUrl &&
572-
isEntityIdExternal({ id: entity.iconUrl }) && (
573-
<CollapsibleSection
574-
togglerClassName="!text-sm !text-primary"
575-
name={t('Files')}
576-
openByDefault
577-
dataQa="files-to-send-request"
578-
className="!pl-0"
579-
>
580-
<ErrorMessage
581-
type="warning"
582-
error={t(
583-
`The icon used for this application is in the "${isEntityIdPublic({ id: entity.iconUrl }) ? 'Organization' : 'Shared with me'}" section and cannot be published. Please replace the icon, otherwise the application will be published with the default one.`,
584-
)}
585-
/>
586-
</CollapsibleSection>
587-
)}
588-
</>
543+
<ApplicationPublishItems
544+
entity={entity as PublishRequestDialAIEntityModel}
545+
handleSelectItems={handleSelectItems}
546+
publishAction={publishAction}
547+
chosenItemsIds={chosenItemsIds}
548+
/>
589549
)}
590550
</div>
591551
);

apps/chat/src/components/Chat/Publish/PublishWizard.tsx

+53-31
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@ import { getFolderIdFromEntityId } from '@/src/utils/app/folders';
2020
import {
2121
getIdWithoutRootPathSegments,
2222
getRootId,
23-
isEntityIdExternal,
23+
isApplicationId,
2424
} from '@/src/utils/app/id';
2525
import { EnumMapper } from '@/src/utils/app/mappers';
2626
import {
2727
createTargetUrl,
28+
getApplicationPublishResources,
2829
isEntityIdPublic,
2930
} from '@/src/utils/app/publications';
3031
import { NotReplayFilter } from '@/src/utils/app/search';
@@ -41,6 +42,10 @@ import {
4142
import { SharingType } from '@/src/types/share';
4243
import { Translation } from '@/src/types/translation';
4344

45+
import {
46+
ApplicationActions,
47+
ApplicationSelectors,
48+
} from '@/src/store/application/application.reducers';
4449
import { ConversationsSelectors } from '@/src/store/conversations/conversations.reducers';
4550
import { useAppDispatch, useAppSelector } from '@/src/store/hooks';
4651
import {
@@ -114,6 +119,9 @@ export function PublishModal<
114119
const areConversationsWithContentUploading = useAppSelector(
115120
ConversationsSelectors.selectAreConversationsWithContentUploading,
116121
);
122+
const isApplicationLoading = useAppSelector(
123+
ApplicationSelectors.selectIsApplicationLoading,
124+
);
117125
const isRulesLoading = useAppSelector(
118126
PublicationSelectors.selectIsRulesLoading,
119127
);
@@ -129,6 +137,11 @@ export function PublishModal<
129137
const selectedItemsIds = useAppSelector(
130138
PublicationSelectors.selectSelectedItemsToPublish,
131139
);
140+
const applicationDetails = useAppSelector(
141+
ApplicationSelectors.selectApplicationDetail,
142+
);
143+
144+
const applicationId = isApplicationId(entity?.id) ? entity.id : null;
132145

133146
const filteredFiles = useMemo(() => {
134147
if (publishAction === PublishActions.DELETE) {
@@ -168,6 +181,12 @@ export function PublishModal<
168181
}
169182
}, [dispatch, path]);
170183

184+
useEffect(() => {
185+
if (applicationId && isOpen) {
186+
dispatch(ApplicationActions.get({ applicationId }));
187+
}
188+
}, [applicationId, dispatch, isOpen]);
189+
171190
useEffect(() => {
172191
if (currentFolderRules) {
173192
setOtherTargetAudienceFilters(
@@ -312,6 +331,7 @@ export function PublishModal<
312331
: selectedFiles.reduce<PublicationRequestModel['resources']>(
313332
(acc, file) => {
314333
const decodedFileId = ApiUtils.decodeApiUrl(file.id);
334+
315335
const item = mappedFiles.find(
316336
(f) => f.oldUrl === decodedFileId,
317337
);
@@ -322,34 +342,34 @@ export function PublishModal<
322342
sourceUrl: decodedFileId,
323343
targetUrl: item.newUrl,
324344
});
345+
} else {
346+
acc.push({
347+
action: publishAction,
348+
sourceUrl: decodedFileId,
349+
targetUrl: ApiUtils.decodeApiUrl(
350+
constructPath(
351+
file.id.split('/')[0],
352+
PUBLIC_URL_PREFIX,
353+
trimmedPath,
354+
getIdWithoutRootPathSegments(entity.folderId),
355+
file.id.split('/').at(-1),
356+
),
357+
),
358+
});
325359
}
326360

327361
return acc;
328362
},
329363
[],
330364
)),
331-
...(type === SharingType.Application &&
332-
'iconUrl' in entity &&
333-
entity.iconUrl &&
334-
!isEntityIdExternal({ id: entity.iconUrl })
335-
? [
336-
{
337-
action: publishAction,
338-
targetUrl: ApiUtils.decodeApiUrl(
339-
constructPath(
340-
entity.iconUrl.split('/')[0],
341-
PUBLIC_URL_PREFIX,
342-
trimmedPath,
343-
getIdWithoutRootPathSegments(entity.folderId),
344-
entity.iconUrl.split('/').at(-1),
345-
),
346-
),
347-
sourceUrl:
348-
publishAction === PublishActions.DELETE
349-
? undefined
350-
: ApiUtils.decodeApiUrl(entity.iconUrl),
351-
},
352-
]
365+
...(type === SharingType.Application
366+
? getApplicationPublishResources({
367+
entity: entity as PublishRequestDialAIEntityModel,
368+
path: trimmedPath,
369+
publishAction,
370+
applicationDetails,
371+
selectedIds: selectedItemsIds,
372+
})
353373
: []),
354374
],
355375
rules: preparedFilters.map((filter) => ({
@@ -363,18 +383,19 @@ export function PublishModal<
363383
onClose();
364384
},
365385
[
386+
entity,
387+
path,
388+
publishRequestName,
389+
otherTargetAudienceFilters,
366390
currentFolderRules,
367-
dispatch,
368391
entitiesArray,
369-
entity,
370392
filteredFiles,
371-
onClose,
372-
otherTargetAudienceFilters,
373-
path,
393+
type,
394+
dispatch,
374395
publishAction,
375-
publishRequestName,
396+
applicationDetails,
376397
selectedItemsIds,
377-
type,
398+
onClose,
378399
],
379400
);
380401

@@ -417,7 +438,8 @@ export function PublishModal<
417438
isRuleSetterOpened ||
418439
isNothingSelectedAndNoRuleChanges ||
419440
isSomeVersionInvalid ||
420-
areConversationsWithContentUploading;
441+
areConversationsWithContentUploading ||
442+
isApplicationLoading;
421443
const isSendBtnTooltipHidden =
422444
!!publishRequestName.trim().length &&
423445
!isRuleSetterOpened &&

0 commit comments

Comments
 (0)