multi-content-type-relation 0.1.0 → 2.1.0
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/dist/_chunks/en-Bk9okOMP.js +32 -0
- package/dist/_chunks/en-Cj4T04Z2.mjs +32 -0
- package/dist/_chunks/fr-KHPiQOFP.mjs +32 -0
- package/dist/_chunks/fr-ZS3aTnjj.js +32 -0
- package/dist/_chunks/index-BHcolZ4N.mjs +333 -0
- package/dist/_chunks/index-CktBIBSM.js +332 -0
- package/dist/_chunks/index-CxWt3llJ.js +3566 -0
- package/dist/_chunks/index-D6nv39Fp.mjs +3564 -0
- package/dist/admin/index.js +3 -0
- package/dist/admin/index.mjs +4 -0
- package/dist/admin/src/components/Input/InputContentSuggestions.d.ts +13 -0
- package/dist/admin/src/components/Input/MainInput.d.ts +16 -0
- package/dist/admin/src/components/Input/PublicationState.d.ts +6 -0
- package/dist/admin/src/components/Input/TableItem.d.ts +12 -0
- package/dist/admin/src/components/Input/index.d.ts +2 -0
- package/dist/admin/src/components/PluginIcon/index.d.ts +2 -0
- package/dist/admin/src/components/SidePanel/SidePanel.d.ts +7 -0
- package/dist/admin/src/helpers/content.d.ts +5 -0
- package/dist/admin/src/helpers/storage.d.ts +15 -0
- package/dist/admin/src/hooks/useSearchedEntries.d.ts +6 -0
- package/dist/admin/src/hooks/useTranslate.d.ts +4 -0
- package/dist/admin/src/index.d.ts +9 -0
- package/dist/admin/src/interface.d.ts +33 -0
- package/dist/admin/src/pluginId.d.ts +2 -0
- package/dist/server/index.js +506 -25
- package/dist/server/index.mjs +509 -0
- package/dist/server/src/bootstrap.d.ts +5 -0
- package/dist/server/src/config/index.d.ts +13 -0
- package/dist/server/src/content-types/index.d.ts +35 -0
- package/dist/server/src/content-types/mctr-relation/index.d.ts +33 -0
- package/dist/server/src/content-types/mctr-relation/schema.d.ts +31 -0
- package/dist/server/src/controllers/controller.d.ts +27 -0
- package/dist/server/src/controllers/index.d.ts +28 -0
- package/dist/server/src/destroy.d.ts +5 -0
- package/dist/server/src/helpers/index.d.ts +2 -0
- package/dist/server/src/index.d.ts +113 -0
- package/dist/server/src/interface.d.ts +42 -0
- package/dist/server/src/middlewares/index.d.ts +4 -0
- package/dist/server/src/middlewares/middleware.d.ts +2 -0
- package/dist/server/src/policies/index.d.ts +2 -0
- package/dist/server/src/register.d.ts +5 -0
- package/dist/server/src/routes/index.d.ts +18 -0
- package/dist/server/src/services/index.d.ts +8 -0
- package/dist/server/src/services/service.d.ts +7 -0
- package/dist/server/src/utils.d.ts +3 -0
- package/package.json +55 -27
- package/TODO.md +0 -4
- package/admin/src/components/Input/InputContentSuggestions.tsx +0 -162
- package/admin/src/components/Input/MainInput.tsx +0 -135
- package/admin/src/components/Input/PublicationState.tsx +0 -28
- package/admin/src/components/Input/TableItem.tsx +0 -109
- package/admin/src/components/Input/index.tsx +0 -27
- package/admin/src/components/PluginIcon/index.tsx +0 -12
- package/admin/src/helpers/content.ts +0 -60
- package/admin/src/helpers/storage.ts +0 -32
- package/admin/src/hooks/useSearchedEntries.ts +0 -41
- package/admin/src/index.tsx +0 -140
- package/admin/src/interface.ts +0 -37
- package/admin/src/pluginId.ts +0 -5
- package/admin/src/translations/en.json +0 -1
- package/admin/src/translations/fr.json +0 -1
- package/admin/src/utils/getTrad.ts +0 -5
- package/dist/server/bootstrap.js +0 -5
- package/dist/server/config/index.js +0 -27
- package/dist/server/content-types/index.js +0 -3
- package/dist/server/controllers/controller.js +0 -92
- package/dist/server/controllers/index.js +0 -9
- package/dist/server/destroy.js +0 -5
- package/dist/server/interface.js +0 -2
- package/dist/server/middlewares/index.js +0 -9
- package/dist/server/middlewares/middleware.js +0 -163
- package/dist/server/policies/index.js +0 -3
- package/dist/server/register.js +0 -15
- package/dist/server/routes/index.js +0 -29
- package/dist/server/services/index.js +0 -9
- package/dist/server/services/service.js +0 -8
- package/dist/server/utils.js +0 -15
- package/dist/tsconfig.server.tsbuildinfo +0 -1
- package/server/bootstrap.ts +0 -5
- package/server/config/index.ts +0 -28
- package/server/content-types/index.ts +0 -1
- package/server/controllers/controller.ts +0 -107
- package/server/controllers/index.ts +0 -5
- package/server/destroy.ts +0 -5
- package/server/index.ts +0 -23
- package/server/interface.ts +0 -50
- package/server/middlewares/index.ts +0 -5
- package/server/middlewares/middleware.ts +0 -197
- package/server/policies/index.ts +0 -1
- package/server/register.ts +0 -14
- package/server/routes/index.ts +0 -27
- package/server/services/index.ts +0 -5
- package/server/services/service.ts +0 -11
- package/server/utils.ts +0 -14
- package/strapi-admin.js +0 -3
- package/strapi-server.js +0 -3
- package/tsconfig.json +0 -20
- package/tsconfig.server.json +0 -25
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
register: ({ strapi }: {
|
|
3
|
+
strapi: import("@strapi/types/dist/core").Strapi;
|
|
4
|
+
}) => void;
|
|
5
|
+
bootstrap: ({ strapi }: {
|
|
6
|
+
strapi: import("@strapi/types/dist/core").Strapi;
|
|
7
|
+
}) => void;
|
|
8
|
+
destroy: ({ strapi }: {
|
|
9
|
+
strapi: import("@strapi/types/dist/core").Strapi;
|
|
10
|
+
}) => void;
|
|
11
|
+
config: {
|
|
12
|
+
default: ({ env }: {
|
|
13
|
+
env: any;
|
|
14
|
+
}) => {
|
|
15
|
+
recursive: {
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
maxDepth: number;
|
|
18
|
+
};
|
|
19
|
+
debug: boolean;
|
|
20
|
+
};
|
|
21
|
+
validator(config: any): void;
|
|
22
|
+
};
|
|
23
|
+
controllers: {
|
|
24
|
+
controller: ({ strapi }: {
|
|
25
|
+
strapi: import("@strapi/types/dist/core").Strapi;
|
|
26
|
+
}) => {
|
|
27
|
+
getMatchingContent(ctx: any): Promise<{
|
|
28
|
+
uid: import("@strapi/types/dist/uid").ContentType;
|
|
29
|
+
displayName: string;
|
|
30
|
+
searchableField: string;
|
|
31
|
+
results: import("@strapi/types/dist/modules/documents").AnyDocument[];
|
|
32
|
+
}[]>;
|
|
33
|
+
validateRelations: (ctx: any) => Promise<{
|
|
34
|
+
displayName: any;
|
|
35
|
+
uid: string;
|
|
36
|
+
searchableField: any;
|
|
37
|
+
item: import("@strapi/types/dist/modules/documents").AnyDocument;
|
|
38
|
+
}[]>;
|
|
39
|
+
listContentTypes: () => Promise<Record<string, unknown>[]>;
|
|
40
|
+
fetchRevertRelations: (ctx: any) => Promise<{
|
|
41
|
+
title: any;
|
|
42
|
+
uid: any;
|
|
43
|
+
isSingleType: boolean;
|
|
44
|
+
field: any;
|
|
45
|
+
type: any;
|
|
46
|
+
documentId: any;
|
|
47
|
+
}[]>;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
routes: ({
|
|
51
|
+
method: string;
|
|
52
|
+
path: string;
|
|
53
|
+
handler: string;
|
|
54
|
+
config: {
|
|
55
|
+
policies: any[];
|
|
56
|
+
auth: boolean;
|
|
57
|
+
};
|
|
58
|
+
} | {
|
|
59
|
+
method: string;
|
|
60
|
+
path: string;
|
|
61
|
+
handler: string;
|
|
62
|
+
config: {
|
|
63
|
+
policies: any[];
|
|
64
|
+
auth?: undefined;
|
|
65
|
+
};
|
|
66
|
+
})[];
|
|
67
|
+
services: {
|
|
68
|
+
service: ({ strapi }: {
|
|
69
|
+
strapi: import("@strapi/types/dist/core").Strapi;
|
|
70
|
+
}) => {
|
|
71
|
+
getFirstStringFieldInContentType(contentType: any): string;
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
contentTypes: {
|
|
75
|
+
'mctr-relation': {
|
|
76
|
+
schema: {
|
|
77
|
+
collectionName: string;
|
|
78
|
+
info: {
|
|
79
|
+
singularName: string;
|
|
80
|
+
pluralName: string;
|
|
81
|
+
displayName: string;
|
|
82
|
+
name: string;
|
|
83
|
+
};
|
|
84
|
+
pluginOptions: {
|
|
85
|
+
'content-manager': {
|
|
86
|
+
visible: boolean;
|
|
87
|
+
};
|
|
88
|
+
'content-type-builder': {
|
|
89
|
+
visible: boolean;
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
attributes: {
|
|
93
|
+
sourceUID: {
|
|
94
|
+
type: string;
|
|
95
|
+
required: boolean;
|
|
96
|
+
};
|
|
97
|
+
sourceDocId: {
|
|
98
|
+
type: string;
|
|
99
|
+
required: boolean;
|
|
100
|
+
};
|
|
101
|
+
target: {
|
|
102
|
+
type: string;
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
policies: {};
|
|
109
|
+
middlewares: {
|
|
110
|
+
middleware: (ctx: any, next: any) => Promise<void>;
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
export default _default;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export type StrapiContentTypeDefinition = {
|
|
2
|
+
collectionName: string;
|
|
3
|
+
info: {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
singularName: string;
|
|
7
|
+
pluralName: string;
|
|
8
|
+
displayName: string;
|
|
9
|
+
};
|
|
10
|
+
attributes: Record<string, unknown>;
|
|
11
|
+
};
|
|
12
|
+
export type SelectedEntry = {
|
|
13
|
+
displayName: string;
|
|
14
|
+
searchableField: string;
|
|
15
|
+
uid: string;
|
|
16
|
+
item: {
|
|
17
|
+
id: string;
|
|
18
|
+
[key: string]: any;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
export type FormattedStrapiEntry = {
|
|
22
|
+
uid: string;
|
|
23
|
+
documentId: string;
|
|
24
|
+
};
|
|
25
|
+
export type Configuration = {
|
|
26
|
+
recursive: {
|
|
27
|
+
enabled: boolean;
|
|
28
|
+
maxDepth: number;
|
|
29
|
+
};
|
|
30
|
+
debug: boolean;
|
|
31
|
+
};
|
|
32
|
+
export type AnyEntity = {
|
|
33
|
+
documentId: number | string;
|
|
34
|
+
[key: string]: any;
|
|
35
|
+
};
|
|
36
|
+
export type StrapiResponse = {
|
|
37
|
+
data: AnyEntity | AnyEntity[];
|
|
38
|
+
};
|
|
39
|
+
export type Context = {
|
|
40
|
+
configuration: Configuration;
|
|
41
|
+
publicationState: 'live' | 'preview';
|
|
42
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
declare const _default: ({
|
|
2
|
+
method: string;
|
|
3
|
+
path: string;
|
|
4
|
+
handler: string;
|
|
5
|
+
config: {
|
|
6
|
+
policies: any[];
|
|
7
|
+
auth: boolean;
|
|
8
|
+
};
|
|
9
|
+
} | {
|
|
10
|
+
method: string;
|
|
11
|
+
path: string;
|
|
12
|
+
handler: string;
|
|
13
|
+
config: {
|
|
14
|
+
policies: any[];
|
|
15
|
+
auth?: undefined;
|
|
16
|
+
};
|
|
17
|
+
})[];
|
|
18
|
+
export default _default;
|
package/package.json
CHANGED
|
@@ -1,53 +1,81 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "multi-content-type-relation",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"description": "A multi content type relation plugin for Strapi 5.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
7
|
-
"name": "
|
|
7
|
+
"name": "bleulliette@kaliop.com"
|
|
8
8
|
},
|
|
9
9
|
"maintainers": [
|
|
10
10
|
{
|
|
11
|
-
"name": "
|
|
11
|
+
"name": "bleulliette@kaliop.com"
|
|
12
12
|
}
|
|
13
13
|
],
|
|
14
|
+
"exports": {
|
|
15
|
+
"./package.json": "./package.json",
|
|
16
|
+
"./strapi-admin": {
|
|
17
|
+
"types": "./dist/admin/src/index.d.ts",
|
|
18
|
+
"source": "./admin/src/index.ts",
|
|
19
|
+
"import": "./dist/admin/index.mjs",
|
|
20
|
+
"require": "./dist/admin/index.js",
|
|
21
|
+
"default": "./dist/admin/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./strapi-server": {
|
|
24
|
+
"types": "./dist/server/src/index.d.ts",
|
|
25
|
+
"source": "./server/src/index.ts",
|
|
26
|
+
"import": "./dist/server/index.mjs",
|
|
27
|
+
"require": "./dist/server/index.js",
|
|
28
|
+
"default": "./dist/server/index.js"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist"
|
|
33
|
+
],
|
|
14
34
|
"scripts": {
|
|
15
|
-
"build": "
|
|
16
|
-
"
|
|
35
|
+
"build": "strapi-plugin build",
|
|
36
|
+
"watch": "strapi-plugin watch",
|
|
37
|
+
"watch:link": "strapi-plugin watch:link",
|
|
38
|
+
"verify": "strapi-plugin verify",
|
|
39
|
+
"test:ts:front": "run -T tsc -p lib/admin/tsconfig.json",
|
|
40
|
+
"test:ts:back": "run -T tsc -p lib/server/tsconfig.json"
|
|
17
41
|
},
|
|
18
42
|
"dependencies": {
|
|
19
43
|
"@dnd-kit/sortable": "^7.0.2",
|
|
20
|
-
"@strapi/design-system": "^
|
|
21
|
-
"@strapi/
|
|
22
|
-
"
|
|
23
|
-
"prop-types": "^15.7.2"
|
|
44
|
+
"@strapi/design-system": "^2.0.0-rc.27",
|
|
45
|
+
"@strapi/icons": "^2.0.0-rc.27",
|
|
46
|
+
"react-intl": "^7.1.11"
|
|
24
47
|
},
|
|
25
48
|
"devDependencies": {
|
|
26
|
-
"@strapi/
|
|
27
|
-
"@
|
|
28
|
-
"@
|
|
29
|
-
"@types/react
|
|
30
|
-
"@types/
|
|
31
|
-
"
|
|
32
|
-
"react
|
|
33
|
-
"react-
|
|
34
|
-
"
|
|
35
|
-
"
|
|
49
|
+
"@strapi/sdk-plugin": "^5.3.2",
|
|
50
|
+
"@strapi/strapi": "^5.16.1",
|
|
51
|
+
"@strapi/typescript-utils": "^5.16.1",
|
|
52
|
+
"@types/react": "^19.1.8",
|
|
53
|
+
"@types/react-dom": "^19.1.6",
|
|
54
|
+
"prettier": "^3.6.2",
|
|
55
|
+
"react": "^18.3.1",
|
|
56
|
+
"react-dom": "^18.3.1",
|
|
57
|
+
"react-router-dom": "^6.30.1",
|
|
58
|
+
"styled-components": "^6.1.19",
|
|
59
|
+
"typescript": "^5.8.3"
|
|
36
60
|
},
|
|
37
61
|
"peerDependencies": {
|
|
38
|
-
"@strapi/
|
|
39
|
-
"
|
|
40
|
-
"react
|
|
41
|
-
"react-
|
|
42
|
-
"
|
|
62
|
+
"@strapi/sdk-plugin": "^5.3.2",
|
|
63
|
+
"@strapi/strapi": "^5.16.1",
|
|
64
|
+
"react": "^18.3.1",
|
|
65
|
+
"react-dom": "^18.3.1",
|
|
66
|
+
"react-router-dom": "^6.30.1",
|
|
67
|
+
"styled-components": "^6.1.19"
|
|
43
68
|
},
|
|
44
69
|
"engines": {
|
|
45
|
-
"node": ">=
|
|
70
|
+
"node": ">=20.x.x",
|
|
46
71
|
"npm": ">=6.0.0"
|
|
47
72
|
},
|
|
48
73
|
"strapi": {
|
|
49
74
|
"name": "multi-content-type-relation",
|
|
50
|
-
"description": "
|
|
75
|
+
"description": "A multi content type relation plugin for Strapi 5",
|
|
51
76
|
"kind": "plugin"
|
|
77
|
+
},
|
|
78
|
+
"publishConfig": {
|
|
79
|
+
"registry": "https://registry.npmjs.org/"
|
|
52
80
|
}
|
|
53
81
|
}
|
package/TODO.md
DELETED
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import React, { useMemo } from "react"
|
|
2
|
-
import { Box, Table, Thead, Tr, Th, Tbody, Typography, Divider } from "@strapi/design-system"
|
|
3
|
-
import { DndContext, PointerSensor, closestCenter, useSensor, useSensors } from "@dnd-kit/core"
|
|
4
|
-
import { SortableContext, arrayMove, verticalListSortingStrategy } from "@dnd-kit/sortable"
|
|
5
|
-
|
|
6
|
-
import { MatchingContent, SelectedEntry } from "../../interface"
|
|
7
|
-
import { TableItem } from "./TableItem"
|
|
8
|
-
|
|
9
|
-
type Props = {
|
|
10
|
-
uniqueId: number
|
|
11
|
-
suggestions?: MatchingContent[]
|
|
12
|
-
selected?: SelectedEntry[]
|
|
13
|
-
onAddEntry?(entry: SelectedEntry): void
|
|
14
|
-
onDeleteEntry?(entry: SelectedEntry): void
|
|
15
|
-
onEntriesSorted?(entries: SelectedEntry[]): void
|
|
16
|
-
sortable?: boolean
|
|
17
|
-
maximum?: number
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function InputContentSuggestions({
|
|
21
|
-
uniqueId,
|
|
22
|
-
suggestions,
|
|
23
|
-
selected,
|
|
24
|
-
onAddEntry,
|
|
25
|
-
onDeleteEntry,
|
|
26
|
-
onEntriesSorted,
|
|
27
|
-
maximum,
|
|
28
|
-
sortable
|
|
29
|
-
}: Props) {
|
|
30
|
-
const suggestionAsSelectedEntry = useMemo(() => {
|
|
31
|
-
return (suggestions || [])
|
|
32
|
-
.flatMap((suggestion) =>
|
|
33
|
-
suggestion.results.map<SelectedEntry>((entrySuggestion) => ({
|
|
34
|
-
displayName: suggestion.displayName,
|
|
35
|
-
item: entrySuggestion,
|
|
36
|
-
searchableField: suggestion.searchableField,
|
|
37
|
-
uid: suggestion.uid
|
|
38
|
-
}))
|
|
39
|
-
)
|
|
40
|
-
.slice(0, 10)
|
|
41
|
-
}, [suggestions])
|
|
42
|
-
|
|
43
|
-
const buildSelectedId = (entry: SelectedEntry) => {
|
|
44
|
-
return `${uniqueId}-${entry.uid}-${entry.item.id}`
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const availableSuggestions = useMemo(() => {
|
|
48
|
-
const selectedIdentifiers = (selected || []).map(buildSelectedId)
|
|
49
|
-
|
|
50
|
-
return suggestionAsSelectedEntry.filter((suggestion) => !selectedIdentifiers.includes(buildSelectedId(suggestion)))
|
|
51
|
-
}, [suggestions, selected])
|
|
52
|
-
|
|
53
|
-
const onAdd = (entry: SelectedEntry) => {
|
|
54
|
-
if (typeof onAddEntry === "function") {
|
|
55
|
-
onAddEntry(entry)
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const onDelete = (entry: SelectedEntry) => {
|
|
60
|
-
if (typeof onDeleteEntry === "function") {
|
|
61
|
-
onDeleteEntry(entry)
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Sortable behavior
|
|
66
|
-
const onSort = (entries: SelectedEntry[]) => {
|
|
67
|
-
if (typeof onEntriesSorted === "function") {
|
|
68
|
-
onEntriesSorted(entries)
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const sensors = useSensors(
|
|
73
|
-
useSensor(PointerSensor, {
|
|
74
|
-
activationConstraint: {
|
|
75
|
-
distance: 5
|
|
76
|
-
}
|
|
77
|
-
})
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
const handleDragEnd = (event) => {
|
|
81
|
-
const { active, over } = event
|
|
82
|
-
|
|
83
|
-
if (!active || !over) return
|
|
84
|
-
|
|
85
|
-
if (active.id !== over.id) {
|
|
86
|
-
const oldIndex = selected!.findIndex((entry) => buildSelectedId(entry) === active.id)
|
|
87
|
-
const newIndex = selected!.findIndex((entry) => buildSelectedId(entry) === over.id)
|
|
88
|
-
|
|
89
|
-
onSort(arrayMove(selected!, oldIndex, newIndex))
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (!availableSuggestions?.length && !selected?.length) return null
|
|
94
|
-
|
|
95
|
-
return (
|
|
96
|
-
<Box padding={[2, 0, 2, 0]} background="neutral100">
|
|
97
|
-
<Table style={{ whiteSpace: "unset" }}>
|
|
98
|
-
<Thead>
|
|
99
|
-
<Tr>
|
|
100
|
-
<Th></Th>
|
|
101
|
-
<Th>
|
|
102
|
-
<Typography variant="sigma">Title</Typography>
|
|
103
|
-
</Th>
|
|
104
|
-
<Th>
|
|
105
|
-
<Typography variant="sigma">ID</Typography>
|
|
106
|
-
</Th>
|
|
107
|
-
<Th>
|
|
108
|
-
<Typography variant="sigma">Content type</Typography>
|
|
109
|
-
</Th>
|
|
110
|
-
<Th>
|
|
111
|
-
<Typography variant="sigma">State</Typography>
|
|
112
|
-
</Th>
|
|
113
|
-
</Tr>
|
|
114
|
-
</Thead>
|
|
115
|
-
|
|
116
|
-
<Tbody>
|
|
117
|
-
{selected?.length ? (
|
|
118
|
-
sortable ? (
|
|
119
|
-
<>
|
|
120
|
-
{/* @ts-expect-error Server Component */}
|
|
121
|
-
<DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
|
|
122
|
-
<SortableContext
|
|
123
|
-
items={selected.map((entry) => buildSelectedId(entry))}
|
|
124
|
-
strategy={verticalListSortingStrategy}
|
|
125
|
-
>
|
|
126
|
-
{selected.map((entry) => (
|
|
127
|
-
<TableItem
|
|
128
|
-
uniqueId={uniqueId}
|
|
129
|
-
key={buildSelectedId(entry)}
|
|
130
|
-
entry={entry}
|
|
131
|
-
type="selected"
|
|
132
|
-
onDelete={onDelete}
|
|
133
|
-
sortable={sortable}
|
|
134
|
-
/>
|
|
135
|
-
))}
|
|
136
|
-
</SortableContext>
|
|
137
|
-
</DndContext>
|
|
138
|
-
</>
|
|
139
|
-
) : (
|
|
140
|
-
(selected || []).map((entry) => (
|
|
141
|
-
<TableItem uniqueId={uniqueId} entry={entry} type="selected" onDelete={onDelete} />
|
|
142
|
-
))
|
|
143
|
-
)
|
|
144
|
-
) : null}
|
|
145
|
-
|
|
146
|
-
{availableSuggestions.length && selected?.length ? <Tr style={{ height: "32px" }} /> : null}
|
|
147
|
-
|
|
148
|
-
{availableSuggestions.map((entry) => (
|
|
149
|
-
<TableItem
|
|
150
|
-
uniqueId={uniqueId}
|
|
151
|
-
key={buildSelectedId(entry)}
|
|
152
|
-
entry={entry}
|
|
153
|
-
type="suggestion"
|
|
154
|
-
onAdd={onAdd}
|
|
155
|
-
disabled={typeof maximum === "number" ? (selected?.length ?? 0) >= maximum : false}
|
|
156
|
-
/>
|
|
157
|
-
))}
|
|
158
|
-
</Tbody>
|
|
159
|
-
</Table>
|
|
160
|
-
</Box>
|
|
161
|
-
)
|
|
162
|
-
}
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useMemo, useState } from "react"
|
|
2
|
-
import { useIntl } from "react-intl"
|
|
3
|
-
import { useLocation } from "react-router-dom"
|
|
4
|
-
|
|
5
|
-
import { Loader, Field, TextInput } from "@strapi/design-system"
|
|
6
|
-
|
|
7
|
-
import { FormattedStrapiEntry, PluginOption, SelectedEntry } from "../../interface"
|
|
8
|
-
import { useSearchedEntries } from "../../hooks/useSearchedEntries"
|
|
9
|
-
import { InputContentSuggestions } from "./InputContentSuggestions"
|
|
10
|
-
import { formatToStrapiField, validateCurrentRelations } from "../../helpers/content"
|
|
11
|
-
|
|
12
|
-
type Props = {
|
|
13
|
-
name: string
|
|
14
|
-
error: string
|
|
15
|
-
description: string
|
|
16
|
-
value: string
|
|
17
|
-
onChange(payload: Record<string, unknown>): void
|
|
18
|
-
intlLabel: Record<string, string>
|
|
19
|
-
attribute: PluginOption
|
|
20
|
-
required: boolean
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function MainInput({ name, error, description, onChange, value, intlLabel, attribute, required }: Props) {
|
|
24
|
-
const { formatMessage } = useIntl()
|
|
25
|
-
const location = useLocation()
|
|
26
|
-
const maximumItems = attribute.options.max
|
|
27
|
-
const minimumItems = attribute.options.min || 0
|
|
28
|
-
const currentLocale = new URLSearchParams(location.search).get("plugins[i18n][locale]") as string
|
|
29
|
-
|
|
30
|
-
const [keyword, setKeyword] = useState("")
|
|
31
|
-
const [selected, setSelected] = useState<SelectedEntry[]>([])
|
|
32
|
-
const [loading, setLoading] = useState(true)
|
|
33
|
-
|
|
34
|
-
useEffect(() => {
|
|
35
|
-
const value = selected.length > maximumItems || selected.length < minimumItems ? [] : selected
|
|
36
|
-
|
|
37
|
-
onChange({ target: { name, value: formatToStrapiField(value) } })
|
|
38
|
-
}, [selected])
|
|
39
|
-
|
|
40
|
-
const hint = useMemo(() => {
|
|
41
|
-
const minLabel = minimumItems > 0 ? `min. ${minimumItems} ${minimumItems > 1 ? "entries" : "entry"}` : ""
|
|
42
|
-
const maxLabel = maximumItems > 0 ? `max. ${maximumItems} ${maximumItems > 1 ? "entries" : "entry"}` : ""
|
|
43
|
-
|
|
44
|
-
return `
|
|
45
|
-
${minLabel ? `${minLabel}` : ""}
|
|
46
|
-
${minLabel && maxLabel ? ", " : ""}
|
|
47
|
-
${maxLabel}
|
|
48
|
-
${minLabel || maxLabel ? " - " : ""}
|
|
49
|
-
${selected.length} selected
|
|
50
|
-
`
|
|
51
|
-
}, [selected, maximumItems, minimumItems])
|
|
52
|
-
|
|
53
|
-
const inputError = useMemo(() => {
|
|
54
|
-
if (!error) return ""
|
|
55
|
-
|
|
56
|
-
if (selected.length < minimumItems) return `${error} - A minimum of ${minimumItems} item(s) is required`
|
|
57
|
-
|
|
58
|
-
if (selected.length > maximumItems) return `${error} - A maximum of ${maximumItems} item(s) is required`
|
|
59
|
-
|
|
60
|
-
return error
|
|
61
|
-
}, [error, maximumItems, minimumItems, selected])
|
|
62
|
-
|
|
63
|
-
const { loading: searchLoading, results } = useSearchedEntries(keyword, attribute.options.contentTypes, currentLocale)
|
|
64
|
-
|
|
65
|
-
// Validate relations and remove the one that got deleted
|
|
66
|
-
useEffect(() => {
|
|
67
|
-
async function validateContent() {
|
|
68
|
-
if (!value) {
|
|
69
|
-
setLoading(false)
|
|
70
|
-
return
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const entries = JSON.parse(value) as FormattedStrapiEntry[]
|
|
74
|
-
|
|
75
|
-
const result = await validateCurrentRelations(entries)
|
|
76
|
-
|
|
77
|
-
setSelected(result)
|
|
78
|
-
setLoading(false)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
validateContent()
|
|
82
|
-
}, [])
|
|
83
|
-
|
|
84
|
-
const onAddEntry = (entry: SelectedEntry) => {
|
|
85
|
-
const alreadyDefined = selected.some(
|
|
86
|
-
(selectedEntry) => selectedEntry.uid === entry.uid && selectedEntry.item.id === entry.item.id
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
if (alreadyDefined) return
|
|
90
|
-
|
|
91
|
-
setSelected([...selected, entry])
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const onDeleteEntry = (entry: SelectedEntry) => {
|
|
95
|
-
const newSelected = selected.filter(
|
|
96
|
-
(selectedEntry) => !(selectedEntry.uid === entry.uid && selectedEntry.item.id === entry.item.id)
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
setSelected(newSelected)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const onEntriesSorted = (entries) => {
|
|
103
|
-
setSelected(entries)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (loading) return <Loader />
|
|
107
|
-
|
|
108
|
-
return (
|
|
109
|
-
<Field name={name} id={name} error={error} hint={description} required={required}>
|
|
110
|
-
<TextInput
|
|
111
|
-
label={formatMessage(intlLabel)}
|
|
112
|
-
placeholder="Type a term to search"
|
|
113
|
-
required={required}
|
|
114
|
-
hint={hint}
|
|
115
|
-
error={inputError}
|
|
116
|
-
value={keyword}
|
|
117
|
-
onChange={(e) => setKeyword(e.target.value)}
|
|
118
|
-
/>
|
|
119
|
-
{searchLoading ? (
|
|
120
|
-
<Loader />
|
|
121
|
-
) : (
|
|
122
|
-
<InputContentSuggestions
|
|
123
|
-
uniqueId={Date.now()}
|
|
124
|
-
suggestions={results}
|
|
125
|
-
selected={selected}
|
|
126
|
-
onAddEntry={onAddEntry}
|
|
127
|
-
onDeleteEntry={onDeleteEntry}
|
|
128
|
-
onEntriesSorted={onEntriesSorted}
|
|
129
|
-
maximum={maximumItems}
|
|
130
|
-
sortable
|
|
131
|
-
/>
|
|
132
|
-
)}
|
|
133
|
-
</Field>
|
|
134
|
-
)
|
|
135
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import React, { useMemo } from "react"
|
|
2
|
-
import { Status, Typography } from "@strapi/design-system"
|
|
3
|
-
|
|
4
|
-
type Props = {
|
|
5
|
-
isPublished: boolean
|
|
6
|
-
hasDraftAndPublish?: boolean
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export const PublicationState = ({ isPublished, hasDraftAndPublish }: Props) => {
|
|
10
|
-
const configuration = useMemo(() => {
|
|
11
|
-
const conf = { variant: "alternative", text: "N/A" }
|
|
12
|
-
|
|
13
|
-
if (hasDraftAndPublish) {
|
|
14
|
-
conf.variant = isPublished ? "success" : "secondary"
|
|
15
|
-
conf.text = isPublished ? "Published" : "Draft"
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return conf
|
|
19
|
-
}, [isPublished, hasDraftAndPublish])
|
|
20
|
-
|
|
21
|
-
return (
|
|
22
|
-
<Status showBullet={false} variant={configuration.variant} size="S" width="min-content">
|
|
23
|
-
<Typography fontWeight="bold" textColor={`${configuration.variant}700`}>
|
|
24
|
-
{configuration.text}
|
|
25
|
-
</Typography>
|
|
26
|
-
</Status>
|
|
27
|
-
)
|
|
28
|
-
}
|