@webbio/strapi-plugin-page-builder 0.12.8-platform → 0.13.0-platform
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/admin/src/api/page-type-exists.ts +44 -0
- package/admin/src/api/search-filtered-entity.ts +22 -6
- package/admin/src/components/EditView/CollectionTypeSettings/index.tsx +17 -6
- package/admin/src/components/EditView/Details/styles.ts +11 -1
- package/admin/src/components/PageFilters/filters.tsx +18 -10
- package/admin/src/components/PlatformFilteredSelectField/Multi/index.tsx +10 -8
- package/admin/src/components/PlatformFilteredSelectField/Single/index.tsx +31 -17
- package/admin/src/components/PlatformFilteredSelectField/styles.tsx +20 -1
- package/admin/src/components/PlatformFilteredSelectField/utils/relation-helper.ts +5 -1
- package/admin/src/components/StrapiCore/admin/admin/src/content-manager/components/Relations/RelationInput.tsx +41 -15
- package/admin/src/utils/hooks/useQueryParams.ts +52 -0
- package/dist/package.json +1 -1
- package/dist/server/bootstrap/permissions.js +19 -16
- package/dist/server/controllers/private-content.js +1 -1
- package/dist/tsconfig.server.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/server/bootstrap/permissions.ts +21 -17
- package/server/controllers/private-content.ts +1 -1
package/README.md
CHANGED
|
@@ -137,7 +137,11 @@ Ik wil een lijst van vacature relaties selecteren. Deze vacatures moeten van het
|
|
|
137
137
|
// (Default: true). Disables result filtering by globally selected platform
|
|
138
138
|
"disablePlatformFilter": boolean,
|
|
139
139
|
// (Default: false) Hides id of the entity that is edited if it's the same as the targetField uid. So if this is enabled on a vacancy entity and also targets other vacancies, the current vacancy that is edited will not be shown.
|
|
140
|
-
"hideSameEntity": boolean
|
|
140
|
+
"hideSameEntity": boolean,
|
|
141
|
+
// (Default: ['title']) Defines searchable fields
|
|
142
|
+
"searchableFields": string[],
|
|
143
|
+
// If set the field will show a subtitle with the value that results in following this path.
|
|
144
|
+
"subTitlePath": string
|
|
141
145
|
}
|
|
142
146
|
},
|
|
143
147
|
"type": "customField",
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { useQuery, UseQueryOptions } from 'react-query';
|
|
2
|
+
|
|
3
|
+
import { useFetchClient } from '@strapi/helper-plugin';
|
|
4
|
+
|
|
5
|
+
type PageTypeExistsQueryParams = {
|
|
6
|
+
uid: string;
|
|
7
|
+
fetchClient?: any;
|
|
8
|
+
platformId: number;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const QUERY_KEY = 'pageTypeExists';
|
|
12
|
+
|
|
13
|
+
const fetchPageTypeExists = async ({ fetchClient, uid, platformId }: PageTypeExistsQueryParams): Promise<boolean> => {
|
|
14
|
+
try {
|
|
15
|
+
if (!uid) {
|
|
16
|
+
throw new Error('No uid');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!platformId) {
|
|
20
|
+
throw new Error('No platformId');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const { get } = fetchClient;
|
|
24
|
+
const result = await get('/content-manager/collection-types/api::page-type.page-type?page=1&pageSize=999');
|
|
25
|
+
|
|
26
|
+
const pageTypeExists = result?.data?.results.some(
|
|
27
|
+
(entity: Record<string, any>) => entity?.platform?.id === platformId && entity.uid === uid
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
return Boolean(pageTypeExists);
|
|
31
|
+
} catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const usePageTypeExists = (params: PageTypeExistsQueryParams, options?: UseQueryOptions<boolean, Error>) => {
|
|
37
|
+
const fetchClient = useFetchClient();
|
|
38
|
+
|
|
39
|
+
return useQuery<boolean, Error>(
|
|
40
|
+
[QUERY_KEY, params?.uid, params.platformId],
|
|
41
|
+
() => fetchPageTypeExists({ ...params, fetchClient }),
|
|
42
|
+
options
|
|
43
|
+
);
|
|
44
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useQuery, UseQueryOptions } from 'react-query';
|
|
2
2
|
import orderBy from 'lodash/orderBy';
|
|
3
|
+
import objGet from 'lodash/get';
|
|
3
4
|
|
|
4
5
|
import { useFetchClient } from '@strapi/helper-plugin';
|
|
5
6
|
import qs from 'qs';
|
|
@@ -17,6 +18,7 @@ export type SearchFilteredEntitiesResult = {
|
|
|
17
18
|
href: string;
|
|
18
19
|
publicationState?: string | false;
|
|
19
20
|
publishedAt?: string;
|
|
21
|
+
subTitle?: string;
|
|
20
22
|
}[];
|
|
21
23
|
};
|
|
22
24
|
|
|
@@ -29,6 +31,9 @@ type SearchFilteredEntitiesQueryParams = {
|
|
|
29
31
|
platformTitle?: string;
|
|
30
32
|
notIds?: number[];
|
|
31
33
|
customFilters?: Record<string, any>[];
|
|
34
|
+
searchableFields?: string[];
|
|
35
|
+
subTitlePath?: string;
|
|
36
|
+
mainFieldName?: string;
|
|
32
37
|
};
|
|
33
38
|
|
|
34
39
|
const QUERY_KEY = 'filteredEntities';
|
|
@@ -41,22 +46,30 @@ export const getSearchFilteredEntities = async ({
|
|
|
41
46
|
searchQuery,
|
|
42
47
|
platformTitle,
|
|
43
48
|
notIds,
|
|
44
|
-
customFilters
|
|
49
|
+
customFilters,
|
|
50
|
+
searchableFields = ['title'],
|
|
51
|
+
subTitlePath,
|
|
52
|
+
mainFieldName = 'title'
|
|
45
53
|
}: SearchFilteredEntitiesQueryParams): Promise<SearchFilteredEntitiesResult> => {
|
|
46
54
|
try {
|
|
47
55
|
const { get } = fetchClient;
|
|
48
56
|
const notIdFilters = notIds && notIds.length > 0 ? notIds.map((id) => ({ id: { $ne: id } })) : [];
|
|
49
57
|
const customFilterObject = Array.isArray(customFilters) ? customFilters : [];
|
|
50
58
|
|
|
59
|
+
const fieldFilters = searchableFields.map((field) => ({ [field]: { $containsi: searchQuery } }));
|
|
60
|
+
|
|
51
61
|
const filters = qs.stringify({
|
|
52
62
|
page,
|
|
53
63
|
pageSize: 20,
|
|
54
64
|
locale,
|
|
55
|
-
|
|
56
|
-
_q: searchQuery || undefined,
|
|
65
|
+
// _q is a fuzzy search with sometimes unexpected results
|
|
66
|
+
// _q: searchQuery || undefined,
|
|
57
67
|
filters: {
|
|
58
68
|
$and: [
|
|
59
|
-
{ platform: { title: { $
|
|
69
|
+
{ platform: { title: { $eq: platformTitle || undefined } } },
|
|
70
|
+
{
|
|
71
|
+
$or: fieldFilters
|
|
72
|
+
},
|
|
60
73
|
...notIdFilters,
|
|
61
74
|
...customFilterObject
|
|
62
75
|
]
|
|
@@ -74,12 +87,15 @@ export const getSearchFilteredEntities = async ({
|
|
|
74
87
|
return false;
|
|
75
88
|
};
|
|
76
89
|
|
|
90
|
+
const subTitle = subTitlePath ? objGet(result, subTitlePath) : undefined;
|
|
91
|
+
|
|
77
92
|
return {
|
|
78
93
|
id: result.id,
|
|
79
|
-
title: result
|
|
94
|
+
title: result?.[mainFieldName],
|
|
80
95
|
publicationState: getPublicationState(),
|
|
81
96
|
publishedAt: result?.publishedAt,
|
|
82
|
-
href: `/content-manager/collectionType/${uid}/${result.id}
|
|
97
|
+
href: `/content-manager/collectionType/${uid}/${result.id}`,
|
|
98
|
+
subTitle: typeof subTitle === 'string' || typeof subTitle === 'number' ? String(subTitle) : ''
|
|
83
99
|
};
|
|
84
100
|
}
|
|
85
101
|
);
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import React, { useEffect, useState } from 'react';
|
|
2
2
|
|
|
3
3
|
import { useCMEditViewDataManager } from '@strapi/helper-plugin';
|
|
4
|
-
import { Flex } from '@strapi/design-system';
|
|
5
|
-
import { Link } from '@strapi/icons';
|
|
4
|
+
import { Flex, Typography, Link } from '@strapi/design-system';
|
|
5
|
+
import { Link as LinkIcon } from '@strapi/icons';
|
|
6
6
|
|
|
7
7
|
import { Wrapper } from '../wrapper';
|
|
8
8
|
import { CreatePageButton } from './CreatePageButton';
|
|
9
9
|
import { PAGE_TYPE_PAGE, PAGE_UID } from '../../../../../shared/utils/constants';
|
|
10
10
|
import { usePlatformFormData } from '../../../utils/hooks/usePlatformFormData';
|
|
11
11
|
import S from '../Details/styles';
|
|
12
|
+
import { usePageTypeExists } from '../../../api/page-type-exists';
|
|
12
13
|
|
|
13
14
|
interface CollectionTypeSettingsProps {
|
|
14
15
|
onlyPlatform?: boolean;
|
|
@@ -20,8 +21,11 @@ export const CollectionTypeSettings = ({ onlyPlatform }: CollectionTypeSettingsP
|
|
|
20
21
|
const { layout, isCreatingEntry, initialData, modifiedData, onChange } = form;
|
|
21
22
|
const isUserCreatedContentType = layout.uid.startsWith('api::');
|
|
22
23
|
const [linkedPage, setLinkedPage] = useState<Record<string, any> | undefined>(initialData.page?.[0]);
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
const { data: pageTypeExists, isLoading: isLoadingPageTypeExists } = usePageTypeExists({
|
|
25
|
+
uid: layout.uid,
|
|
26
|
+
platformId: selectedPlatform?.id
|
|
27
|
+
});
|
|
28
|
+
const showCreatePageButton = !linkedPage && isUserCreatedContentType && !isCreatingEntry && !isLoadingPlatform;
|
|
25
29
|
const url = generateLink(linkedPage?.id, initialData?.locale);
|
|
26
30
|
|
|
27
31
|
useEffect(() => {
|
|
@@ -50,7 +54,14 @@ export const CollectionTypeSettings = ({ onlyPlatform }: CollectionTypeSettingsP
|
|
|
50
54
|
{!isCreatingEntry && !onlyPlatform && (
|
|
51
55
|
<Wrapper title="Gekoppelde pagina">
|
|
52
56
|
<Flex direction="column" gap={4} width="100%" alignItems="start">
|
|
53
|
-
{
|
|
57
|
+
{!pageTypeExists && !linkedPage && !isLoadingPageTypeExists && (
|
|
58
|
+
<S.InfoType variant="pi" textColor="neutral800">
|
|
59
|
+
Er bestaat nog geen{' '}
|
|
60
|
+
<Link to="/content-manager/collectionType/api::page-type.page-type">pagina type</Link> voor dit content
|
|
61
|
+
type.
|
|
62
|
+
</S.InfoType>
|
|
63
|
+
)}
|
|
64
|
+
{showCreatePageButton && selectedPlatform && (pageTypeExists || linkedPage) && (
|
|
54
65
|
<CreatePageButton onCreatedPage={(page) => setLinkedPage(page)} selectedPlatform={selectedPlatform} />
|
|
55
66
|
)}
|
|
56
67
|
|
|
@@ -61,7 +72,7 @@ export const CollectionTypeSettings = ({ onlyPlatform }: CollectionTypeSettingsP
|
|
|
61
72
|
</S.SubtleType>
|
|
62
73
|
<S.EntityLinkWrapper variant="pi" textColor="neutral800">
|
|
63
74
|
<S.EntityLink title={linkedPage?.title || '-'} to={url} variant="pi">
|
|
64
|
-
<
|
|
75
|
+
<LinkIcon />
|
|
65
76
|
{linkedPage?.title || '-'}
|
|
66
77
|
</S.EntityLink>
|
|
67
78
|
</S.EntityLinkWrapper>
|
|
@@ -8,6 +8,15 @@ const SubtleType = styled(Typography)`
|
|
|
8
8
|
`}
|
|
9
9
|
`;
|
|
10
10
|
|
|
11
|
+
const InfoType = styled(Typography)`
|
|
12
|
+
${({ theme }) => css`
|
|
13
|
+
&,
|
|
14
|
+
a span {
|
|
15
|
+
font-size: ${theme.fontSizes[1]};
|
|
16
|
+
}
|
|
17
|
+
`}
|
|
18
|
+
`;
|
|
19
|
+
|
|
11
20
|
const EntityLinkWrapper = styled(Typography)`
|
|
12
21
|
${({ _theme }) => css`
|
|
13
22
|
display: flex;
|
|
@@ -45,7 +54,8 @@ const DetailsStyles = {
|
|
|
45
54
|
SubtleType,
|
|
46
55
|
EntityLink,
|
|
47
56
|
EntityLinkWrapper,
|
|
48
|
-
EditLink
|
|
57
|
+
EditLink,
|
|
58
|
+
InfoType
|
|
49
59
|
};
|
|
50
60
|
|
|
51
61
|
export default DetailsStyles;
|
|
@@ -2,7 +2,6 @@ import { useEffect, useMemo } from 'react';
|
|
|
2
2
|
import set from 'lodash/set';
|
|
3
3
|
|
|
4
4
|
import { Stack } from '@strapi/design-system';
|
|
5
|
-
import { useQueryParams } from '@strapi/helper-plugin';
|
|
6
5
|
|
|
7
6
|
import PageTypeFilter from './PageTypeFilter';
|
|
8
7
|
import { PAGE_TYPE_PAGE, PLATFORM } from '../../../../shared/utils/constants';
|
|
@@ -12,13 +11,14 @@ import { PageType } from '../../api/platform-page-types';
|
|
|
12
11
|
import { useGetPageTypesForPlatform } from '../../api/platform-page-types';
|
|
13
12
|
import { useDefaultPlatformFromLocalStorage } from '../../utils/hooks/useDefaultPlatformFromLocalStorage';
|
|
14
13
|
import { useHideOverviewFilterTags } from '../../utils/hooks/useHideOverviewFilterTags';
|
|
14
|
+
import { useQueryParams } from '../../utils/hooks/useQueryParams';
|
|
15
15
|
|
|
16
16
|
interface PageFiltersProps {
|
|
17
17
|
hidePageType?: boolean;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const PageFilters = ({ hidePageType }: PageFiltersProps) => {
|
|
21
|
-
const [{ query }, setQuery] = useQueryParams()
|
|
21
|
+
const [{ query }, setQuery] = useQueryParams();
|
|
22
22
|
const { selectedPlatform: globalSelectedPlatform } = useDefaultPlatformFromLocalStorage();
|
|
23
23
|
useHideOverviewFilterTags();
|
|
24
24
|
const { data: platforms } = useGetPlatforms({});
|
|
@@ -50,19 +50,27 @@ const PageFilters = ({ hidePageType }: PageFiltersProps) => {
|
|
|
50
50
|
|
|
51
51
|
const removedFilters = removeFiltersFromQuery({ filters: currentFilters }, filterCategoryToRemove);
|
|
52
52
|
|
|
53
|
-
setQuery(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
setQuery(
|
|
54
|
+
{
|
|
55
|
+
page: 1,
|
|
56
|
+
filters: removedFilters
|
|
57
|
+
},
|
|
58
|
+
undefined,
|
|
59
|
+
'replace'
|
|
60
|
+
);
|
|
57
61
|
};
|
|
58
62
|
|
|
59
63
|
const removeFilters = (filterCategory: string) => {
|
|
60
64
|
const filters = removeFiltersFromQuery(query, filterCategory);
|
|
61
65
|
|
|
62
|
-
setQuery(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
setQuery(
|
|
67
|
+
{
|
|
68
|
+
page: 1,
|
|
69
|
+
filters
|
|
70
|
+
},
|
|
71
|
+
undefined,
|
|
72
|
+
'replace'
|
|
73
|
+
);
|
|
66
74
|
};
|
|
67
75
|
|
|
68
76
|
const handlePlatformSelect = (platformTitle: string) => {
|
|
@@ -14,7 +14,6 @@ import { useGetLocaleFromUrl } from '../../../utils/hooks/useGetLocaleFromUrl';
|
|
|
14
14
|
import { getSearchFilteredEntities } from '../../../api/search-filtered-entity';
|
|
15
15
|
import RelationHelper, { IPlatformFilteredSelectFieldProps } from '../utils/relation-helper';
|
|
16
16
|
import { getTranslation } from '../utils/get-translations';
|
|
17
|
-
import { Platform } from '../../../api/platform';
|
|
18
17
|
|
|
19
18
|
const PAGE = 1;
|
|
20
19
|
|
|
@@ -25,8 +24,6 @@ export interface IMultiPlatformFilteredSelectFieldProps extends IPlatformFiltere
|
|
|
25
24
|
isCloningEntry?: boolean;
|
|
26
25
|
totalRelations: number;
|
|
27
26
|
relationsFromModifiedData?: Record<string, any>[];
|
|
28
|
-
selectedPlatform?: Platform;
|
|
29
|
-
hiddenId?: number[];
|
|
30
27
|
}
|
|
31
28
|
|
|
32
29
|
const MultiPlatformFilteredSelectField = ({
|
|
@@ -43,7 +40,8 @@ const MultiPlatformFilteredSelectField = ({
|
|
|
43
40
|
totalRelations,
|
|
44
41
|
relationsFromModifiedData,
|
|
45
42
|
selectedPlatform,
|
|
46
|
-
hiddenId
|
|
43
|
+
hiddenId,
|
|
44
|
+
disabled
|
|
47
45
|
}: IMultiPlatformFilteredSelectFieldProps) => {
|
|
48
46
|
const fetchClient = useFetchClient();
|
|
49
47
|
const {
|
|
@@ -73,7 +71,7 @@ const MultiPlatformFilteredSelectField = ({
|
|
|
73
71
|
|
|
74
72
|
const { formatMessage } = useIntl();
|
|
75
73
|
|
|
76
|
-
const { targetAttributes, targetFieldValue, targetField } = RelationHelper.getTargetAttributes(
|
|
74
|
+
const { targetAttributes, targetFieldValue, targetField, relationMainFieldName } = RelationHelper.getTargetAttributes(
|
|
77
75
|
name,
|
|
78
76
|
attribute,
|
|
79
77
|
modifiedData,
|
|
@@ -102,7 +100,10 @@ const MultiPlatformFilteredSelectField = ({
|
|
|
102
100
|
searchQuery: inputValue,
|
|
103
101
|
platformTitle: attribute?.pluginOptions?.filteredSelect?.disablePlatformFilter ? '' : selectedPlatform?.title,
|
|
104
102
|
notIds: [...(selectedResults?.data || []).map((x: Record<string, any>) => x.id), ...(hiddenId || [])],
|
|
105
|
-
customFilters: attribute?.pluginOptions?.filteredSelect?.customFilters
|
|
103
|
+
customFilters: attribute?.pluginOptions?.filteredSelect?.customFilters,
|
|
104
|
+
searchableFields: attribute?.pluginOptions?.filteredSelect?.searchableFields,
|
|
105
|
+
subTitlePath: attribute?.pluginOptions?.filteredSelect?.subTitlePath,
|
|
106
|
+
mainFieldName: relationMainFieldName
|
|
106
107
|
});
|
|
107
108
|
|
|
108
109
|
const mapped = entities?.results.map((x) => ({
|
|
@@ -110,7 +111,8 @@ const MultiPlatformFilteredSelectField = ({
|
|
|
110
111
|
mainField: x.title,
|
|
111
112
|
href: x?.href,
|
|
112
113
|
publicationState: x.publicationState,
|
|
113
|
-
publishedAt: x.publishedAt
|
|
114
|
+
publishedAt: x.publishedAt,
|
|
115
|
+
subTitle: x.subTitle
|
|
114
116
|
}));
|
|
115
117
|
|
|
116
118
|
// @ts-expect-error data is fine
|
|
@@ -202,7 +204,7 @@ const MultiPlatformFilteredSelectField = ({
|
|
|
202
204
|
hint={hint || ''}
|
|
203
205
|
labelAction={labelAction}
|
|
204
206
|
required={required}
|
|
205
|
-
disabled={!selectedPlatform}
|
|
207
|
+
disabled={!selectedPlatform || disabled}
|
|
206
208
|
/>
|
|
207
209
|
);
|
|
208
210
|
};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import React, { useEffect, useState } from 'react';
|
|
2
2
|
import { useIntl } from 'react-intl';
|
|
3
3
|
import debounce from 'lodash/debounce';
|
|
4
|
+
import objGet from 'lodash/get';
|
|
4
5
|
import { useCMEditViewDataManager } from '@strapi/helper-plugin';
|
|
5
6
|
import { useSelector } from 'react-redux';
|
|
6
|
-
import { Field } from '@strapi/design-system';
|
|
7
|
+
import { Field, Flex } from '@strapi/design-system';
|
|
7
8
|
import { useFetchClient } from '@strapi/helper-plugin';
|
|
8
9
|
import { Link } from '@strapi/icons';
|
|
9
10
|
|
|
@@ -14,17 +15,14 @@ import { getSearchFilteredEntities } from '../../../api/search-filtered-entity';
|
|
|
14
15
|
import S from './../styles';
|
|
15
16
|
import getTrad from '../../../utils/getTrad';
|
|
16
17
|
import RelationHelper, { IPlatformFilteredSelectFieldProps } from '../utils/relation-helper';
|
|
17
|
-
import { Platform } from '../../../api/platform';
|
|
18
18
|
|
|
19
|
-
export interface ISinglePlatformFilteredSelectFieldProps extends IPlatformFilteredSelectFieldProps {
|
|
20
|
-
selectedPlatform?: Platform;
|
|
21
|
-
hiddenId?: number[];
|
|
22
|
-
}
|
|
19
|
+
export interface ISinglePlatformFilteredSelectFieldProps extends IPlatformFilteredSelectFieldProps {}
|
|
23
20
|
|
|
24
21
|
interface CustomReactSelectValue extends Omit<IReactSelectValue, 'initialSelected'> {
|
|
25
22
|
href?: string;
|
|
26
23
|
publicationState?: string | false;
|
|
27
24
|
publishedAt?: string;
|
|
25
|
+
subTitle?: string;
|
|
28
26
|
}
|
|
29
27
|
|
|
30
28
|
const SEARCH_DEBOUNCE_MS = 150;
|
|
@@ -38,7 +36,8 @@ const SinglePlatformFilteredSelectField = ({
|
|
|
38
36
|
intlLabel,
|
|
39
37
|
name,
|
|
40
38
|
selectedPlatform,
|
|
41
|
-
hiddenId
|
|
39
|
+
hiddenId,
|
|
40
|
+
disabled
|
|
42
41
|
}: ISinglePlatformFilteredSelectFieldProps) => {
|
|
43
42
|
const fetchClient = useFetchClient();
|
|
44
43
|
const { onChange, initialData, modifiedData, isCreatingEntry, layout, allLayoutData } =
|
|
@@ -53,7 +52,7 @@ const SinglePlatformFilteredSelectField = ({
|
|
|
53
52
|
|
|
54
53
|
const { formatMessage } = useIntl();
|
|
55
54
|
|
|
56
|
-
const { targetAttributes, targetFieldValue, targetField } = RelationHelper.getTargetAttributes(
|
|
55
|
+
const { targetAttributes, targetFieldValue, targetField, relationMainFieldName } = RelationHelper.getTargetAttributes(
|
|
57
56
|
name,
|
|
58
57
|
attribute,
|
|
59
58
|
modifiedData,
|
|
@@ -70,7 +69,10 @@ const SinglePlatformFilteredSelectField = ({
|
|
|
70
69
|
: '';
|
|
71
70
|
|
|
72
71
|
useEffect(() => {
|
|
73
|
-
const initialSelect = getInitialSelectItem(
|
|
72
|
+
const initialSelect = getInitialSelectItem(
|
|
73
|
+
targetFieldValue?.[0],
|
|
74
|
+
attribute?.pluginOptions?.filteredSelect?.subTitlePath
|
|
75
|
+
);
|
|
74
76
|
|
|
75
77
|
setSelected(initialSelect);
|
|
76
78
|
}, [targetFieldValue]);
|
|
@@ -116,7 +118,10 @@ const SinglePlatformFilteredSelectField = ({
|
|
|
116
118
|
searchQuery: inputValue,
|
|
117
119
|
platformTitle: attribute?.pluginOptions?.filteredSelect?.disablePlatformFilter ? '' : platformTitle,
|
|
118
120
|
customFilters: attribute?.pluginOptions?.filteredSelect?.customFilters,
|
|
119
|
-
notIds: hiddenId
|
|
121
|
+
notIds: hiddenId,
|
|
122
|
+
searchableFields: attribute?.pluginOptions?.filteredSelect?.searchableFields,
|
|
123
|
+
subTitlePath: attribute?.pluginOptions?.filteredSelect?.subTitlePath,
|
|
124
|
+
mainFieldName: relationMainFieldName
|
|
120
125
|
});
|
|
121
126
|
|
|
122
127
|
return pages?.results.map((x) => ({
|
|
@@ -124,7 +129,8 @@ const SinglePlatformFilteredSelectField = ({
|
|
|
124
129
|
label: x.title,
|
|
125
130
|
href: x?.href,
|
|
126
131
|
publicationState: x.publicationState,
|
|
127
|
-
publishedAt: x.publishedAt
|
|
132
|
+
publishedAt: x.publishedAt,
|
|
133
|
+
subTitle: x.subTitle
|
|
128
134
|
}));
|
|
129
135
|
};
|
|
130
136
|
|
|
@@ -153,9 +159,9 @@ const SinglePlatformFilteredSelectField = ({
|
|
|
153
159
|
onChange={handleChange}
|
|
154
160
|
value={selected}
|
|
155
161
|
defaultOptions
|
|
156
|
-
hideSelectedOptions
|
|
162
|
+
// hideSelectedOptions
|
|
157
163
|
labelAction={labelAction}
|
|
158
|
-
isDisabled={!selectedPlatform}
|
|
164
|
+
isDisabled={!selectedPlatform || disabled}
|
|
159
165
|
afterInput={
|
|
160
166
|
<S.LinkToPage
|
|
161
167
|
title={formatMessage({ id: getTrad('platformFilteredSelect.linkToEntity.label') })}
|
|
@@ -172,25 +178,33 @@ const SinglePlatformFilteredSelectField = ({
|
|
|
172
178
|
);
|
|
173
179
|
};
|
|
174
180
|
|
|
175
|
-
const CustomOption = (props: OptionProps<CustomReactSelectValue, false>) => {
|
|
181
|
+
export const CustomOption = (props: OptionProps<CustomReactSelectValue, false>) => {
|
|
176
182
|
return (
|
|
177
183
|
<components.Option {...props}>
|
|
178
184
|
<S.CustomOption>
|
|
179
185
|
{props.data?.publicationState && <S.CustomOptionStatus publicationState={props.data?.publicationState} />}
|
|
180
|
-
|
|
186
|
+
|
|
187
|
+
<Flex direction="column" alignItems="start" gap={1}>
|
|
188
|
+
{props.children}
|
|
189
|
+
{props.data?.subTitle && <S.CustomOptionSubTitle>{props.data?.subTitle}</S.CustomOptionSubTitle>}
|
|
190
|
+
</Flex>
|
|
181
191
|
</S.CustomOption>
|
|
182
192
|
</components.Option>
|
|
183
193
|
);
|
|
184
194
|
};
|
|
185
195
|
|
|
186
|
-
const getInitialSelectItem = (
|
|
196
|
+
const getInitialSelectItem = (
|
|
197
|
+
initialValue?: Record<string, any>,
|
|
198
|
+
subTitlePath?: string
|
|
199
|
+
): SingleValue<CustomReactSelectValue | null> =>
|
|
187
200
|
initialValue?.id
|
|
188
201
|
? {
|
|
189
202
|
value: String(initialValue?.id),
|
|
190
203
|
label: initialValue?.title ?? '',
|
|
191
204
|
href: initialValue?.href,
|
|
192
205
|
publicationState: initialValue.publicationState,
|
|
193
|
-
publishedAt: initialValue.publishedAt
|
|
206
|
+
publishedAt: initialValue.publishedAt,
|
|
207
|
+
subTitle: subTitlePath ? objGet(initialValue, subTitlePath) : ''
|
|
194
208
|
}
|
|
195
209
|
: null;
|
|
196
210
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import styled, { css } from 'styled-components';
|
|
2
|
-
import { Box, Link, Typography } from '@strapi/design-system';
|
|
2
|
+
import { Box, Link, Flex, Typography } from '@strapi/design-system';
|
|
3
3
|
|
|
4
4
|
const EntityLinkWrapper = styled(Typography)`
|
|
5
5
|
${({ _theme }) => css`
|
|
@@ -31,12 +31,29 @@ const CustomOption = styled(Box)`
|
|
|
31
31
|
gap: 8px;
|
|
32
32
|
`;
|
|
33
33
|
|
|
34
|
+
const CustomOptionSubTitle = styled(Box)`
|
|
35
|
+
${({ theme, disabled }) => css`
|
|
36
|
+
font-size: ${theme.fontSizes[1]};
|
|
37
|
+
opacity: 0.5;
|
|
38
|
+
white-space: nowrap;
|
|
39
|
+
overflow: hidden;
|
|
40
|
+
text-overflow: ellipsis;
|
|
41
|
+
width: calc(100%);
|
|
42
|
+
`}
|
|
43
|
+
`;
|
|
44
|
+
const CustomOptionItemWrapper = styled(Flex)`
|
|
45
|
+
> ${CustomOptionSubTitle} {
|
|
46
|
+
margin-top: -2px;
|
|
47
|
+
}
|
|
48
|
+
`;
|
|
49
|
+
|
|
34
50
|
const CustomOptionStatus = styled(Box)`
|
|
35
51
|
background: ${({ theme, publicationState }) =>
|
|
36
52
|
publicationState === 'published' ? theme.colors.success600 : theme.colors.secondary600};
|
|
37
53
|
width: 6px;
|
|
38
54
|
height: 6px;
|
|
39
55
|
border-radius: 100px;
|
|
56
|
+
flex-shrink: 0;
|
|
40
57
|
`;
|
|
41
58
|
|
|
42
59
|
const FieldWrapper = styled(Box)`
|
|
@@ -73,5 +90,7 @@ export default {
|
|
|
73
90
|
EntityLink,
|
|
74
91
|
CustomOptionStatus,
|
|
75
92
|
CustomOption,
|
|
93
|
+
CustomOptionSubTitle,
|
|
94
|
+
CustomOptionItemWrapper,
|
|
76
95
|
LinkToPage
|
|
77
96
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { IntlFormatters, MessageDescriptor } from '@formatjs/intl';
|
|
2
2
|
import get from 'lodash/get';
|
|
3
3
|
import { pointPathToGetPath } from '../../../utils/getObjectFromFormName';
|
|
4
|
+
import { Platform } from '../../../api/platform';
|
|
4
5
|
|
|
5
6
|
export interface GetTargetAttributesResult {
|
|
6
7
|
targetAttributes?: {
|
|
@@ -28,6 +29,8 @@ export interface IPlatformFilteredSelectFieldProps {
|
|
|
28
29
|
customFilters?: Record<string, any>[];
|
|
29
30
|
disablePlatformFilter?: boolean;
|
|
30
31
|
hideSameEntity?: boolean;
|
|
32
|
+
searchableFields?: string[];
|
|
33
|
+
subTitlePath?: string;
|
|
31
34
|
};
|
|
32
35
|
};
|
|
33
36
|
required?: boolean;
|
|
@@ -41,6 +44,8 @@ export interface IPlatformFilteredSelectFieldProps {
|
|
|
41
44
|
value?: string;
|
|
42
45
|
contentTypeUID?: string;
|
|
43
46
|
placeholder?: MessageDescriptor & Parameters<IntlFormatters['formatMessage']>;
|
|
47
|
+
selectedPlatform?: Platform;
|
|
48
|
+
hiddenId?: number[];
|
|
44
49
|
}
|
|
45
50
|
|
|
46
51
|
const RelationHelper = {
|
|
@@ -54,7 +59,6 @@ const RelationHelper = {
|
|
|
54
59
|
const lastFieldName = fieldName?.split('.')?.[fieldName?.split('.')?.length - 1];
|
|
55
60
|
const targetField = attribute?.pluginOptions?.filteredSelect?.targetField || fieldName || '';
|
|
56
61
|
const targetFieldName = (fieldName || '')?.replace(lastFieldName || '', targetField);
|
|
57
|
-
|
|
58
62
|
const component = getComponent(targetFieldName, modifiedData, allLayoutData);
|
|
59
63
|
const result: GetTargetAttributesResult = {
|
|
60
64
|
targetAttributes: undefined,
|
|
@@ -32,6 +32,7 @@ import { getTranslation } from '../../utils/translations';
|
|
|
32
32
|
import type { NormalizedRelation } from './utils/normalizeRelations';
|
|
33
33
|
import type { Contracts } from '../../../../../../content-manager/shared';
|
|
34
34
|
import type { Entity } from '@strapi/types';
|
|
35
|
+
import S from '../../../../../../../PlatformFilteredSelectField/styles';
|
|
35
36
|
|
|
36
37
|
const RELATION_ITEM_HEIGHT = 50;
|
|
37
38
|
const RELATION_GUTTER = 4;
|
|
@@ -81,6 +82,7 @@ interface RelationInputProps
|
|
|
81
82
|
hasNextPage?: boolean;
|
|
82
83
|
};
|
|
83
84
|
size: number;
|
|
85
|
+
mainField?: string;
|
|
84
86
|
}
|
|
85
87
|
|
|
86
88
|
const RelationInput = ({
|
|
@@ -114,6 +116,7 @@ const RelationInput = ({
|
|
|
114
116
|
required,
|
|
115
117
|
relations: paginatedRelations,
|
|
116
118
|
searchResults,
|
|
119
|
+
mainField,
|
|
117
120
|
size
|
|
118
121
|
}: RelationInputProps) => {
|
|
119
122
|
const [textValue, setTextValue] = React.useState<string | undefined>('');
|
|
@@ -265,9 +268,9 @@ const RelationInput = ({
|
|
|
265
268
|
onSearch(event.currentTarget.value);
|
|
266
269
|
}}
|
|
267
270
|
>
|
|
268
|
-
{options.map((opt) =>
|
|
269
|
-
|
|
270
|
-
|
|
271
|
+
{options.map((opt) => (
|
|
272
|
+
<Option key={opt.id} {...opt} />
|
|
273
|
+
))}
|
|
271
274
|
</Combobox>
|
|
272
275
|
</ComboboxWrapper>
|
|
273
276
|
|
|
@@ -309,7 +312,8 @@ const RelationInput = ({
|
|
|
309
312
|
onRelationDisconnect,
|
|
310
313
|
publicationStateTranslations,
|
|
311
314
|
relations,
|
|
312
|
-
updatePositionOfRelation: handleUpdatePositionOfRelation
|
|
315
|
+
updatePositionOfRelation: handleUpdatePositionOfRelation,
|
|
316
|
+
subTitle: 'ho'
|
|
313
317
|
}}
|
|
314
318
|
itemKey={(index) => `${relations[index].mainField}_${relations[index].id}`}
|
|
315
319
|
innerElementType="ol"
|
|
@@ -366,8 +370,9 @@ const ShadowBox = styled(Box)<{ overflowDirection?: 'top-bottom' | 'top' | 'bott
|
|
|
366
370
|
const Option = ({
|
|
367
371
|
publicationState,
|
|
368
372
|
mainField,
|
|
369
|
-
id
|
|
370
|
-
|
|
373
|
+
id,
|
|
374
|
+
subTitle
|
|
375
|
+
}: Pick<NormalizedRelation, 'id' | 'mainField' | 'publicationState'> & { subTitle?: string }) => {
|
|
371
376
|
const { formatMessage } = useIntl();
|
|
372
377
|
const stringifiedDisplayValue = (mainField ?? id).toString();
|
|
373
378
|
|
|
@@ -384,18 +389,32 @@ const Option = ({
|
|
|
384
389
|
const title = isDraft ? formatMessage(draftMessage) : formatMessage(publishedMessage);
|
|
385
390
|
|
|
386
391
|
return (
|
|
387
|
-
<ComboboxOption
|
|
392
|
+
<ComboboxOption
|
|
393
|
+
value={id.toString()}
|
|
394
|
+
textValue={stringifiedDisplayValue}
|
|
395
|
+
style={{ paddingTop: 5, paddingBottom: 5 }}
|
|
396
|
+
>
|
|
388
397
|
<Flex>
|
|
389
398
|
<StyledBullet title={title} isDraft={isDraft} />
|
|
390
|
-
<
|
|
399
|
+
<S.CustomOptionItemWrapper direction="column" alignItems="start">
|
|
400
|
+
<Typography ellipsis>{stringifiedDisplayValue}</Typography>
|
|
401
|
+
{subTitle && <S.CustomOptionSubTitle>{subTitle}</S.CustomOptionSubTitle>}
|
|
402
|
+
</S.CustomOptionItemWrapper>
|
|
391
403
|
</Flex>
|
|
392
404
|
</ComboboxOption>
|
|
393
405
|
);
|
|
394
406
|
}
|
|
395
407
|
|
|
396
408
|
return (
|
|
397
|
-
<ComboboxOption
|
|
398
|
-
{
|
|
409
|
+
<ComboboxOption
|
|
410
|
+
value={id.toString()}
|
|
411
|
+
textValue={stringifiedDisplayValue}
|
|
412
|
+
style={{ paddingTop: 5, paddingBottom: 5 }}
|
|
413
|
+
>
|
|
414
|
+
<S.CustomOptionItemWrapper direction="column" alignItems="start" gap={0}>
|
|
415
|
+
{stringifiedDisplayValue}
|
|
416
|
+
{subTitle && <S.CustomOptionSubTitle>{subTitle}</S.CustomOptionSubTitle>}
|
|
417
|
+
</S.CustomOptionItemWrapper>
|
|
399
418
|
</ComboboxOption>
|
|
400
419
|
);
|
|
401
420
|
};
|
|
@@ -433,6 +452,7 @@ interface ListItemProps extends Pick<RelationItemProps, 'index' | 'style'> {
|
|
|
433
452
|
published: string;
|
|
434
453
|
};
|
|
435
454
|
relations: NormalizedRelation[];
|
|
455
|
+
subTitle?: string;
|
|
436
456
|
};
|
|
437
457
|
}
|
|
438
458
|
|
|
@@ -487,14 +507,20 @@ const ListItem = ({ data, index, style }: ListItemProps) => {
|
|
|
487
507
|
}}
|
|
488
508
|
updatePositionOfRelation={updatePositionOfRelation}
|
|
489
509
|
>
|
|
490
|
-
<Box minWidth={0}
|
|
510
|
+
<Box minWidth={0} paddingRight={4}>
|
|
491
511
|
<Tooltip description={mainField ?? `${id}`}>
|
|
492
512
|
{href ? (
|
|
493
|
-
<
|
|
513
|
+
<Flex direction="column" alignItems="start" gap={0}>
|
|
514
|
+
<LinkEllipsis to={href}>{mainField ?? id}</LinkEllipsis>
|
|
515
|
+
{data?.subTitle && <S.CustomOptionSubTitle>{data.subTitle}</S.CustomOptionSubTitle>}
|
|
516
|
+
</Flex>
|
|
494
517
|
) : (
|
|
495
|
-
<
|
|
496
|
-
{
|
|
497
|
-
|
|
518
|
+
<Flex direction="column" alignItems="start" gap={0}>
|
|
519
|
+
<Typography textColor={disabled ? 'neutral600' : 'primary600'} ellipsis>
|
|
520
|
+
{mainField ?? id}
|
|
521
|
+
</Typography>
|
|
522
|
+
{data?.subTitle && <S.CustomOptionSubTitle>{data.subTitle}</S.CustomOptionSubTitle>}
|
|
523
|
+
</Flex>
|
|
498
524
|
)}
|
|
499
525
|
</Tooltip>
|
|
500
526
|
</Box>
|