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.
Files changed (98) hide show
  1. package/dist/_chunks/en-Bk9okOMP.js +32 -0
  2. package/dist/_chunks/en-Cj4T04Z2.mjs +32 -0
  3. package/dist/_chunks/fr-KHPiQOFP.mjs +32 -0
  4. package/dist/_chunks/fr-ZS3aTnjj.js +32 -0
  5. package/dist/_chunks/index-BHcolZ4N.mjs +333 -0
  6. package/dist/_chunks/index-CktBIBSM.js +332 -0
  7. package/dist/_chunks/index-CxWt3llJ.js +3566 -0
  8. package/dist/_chunks/index-D6nv39Fp.mjs +3564 -0
  9. package/dist/admin/index.js +3 -0
  10. package/dist/admin/index.mjs +4 -0
  11. package/dist/admin/src/components/Input/InputContentSuggestions.d.ts +13 -0
  12. package/dist/admin/src/components/Input/MainInput.d.ts +16 -0
  13. package/dist/admin/src/components/Input/PublicationState.d.ts +6 -0
  14. package/dist/admin/src/components/Input/TableItem.d.ts +12 -0
  15. package/dist/admin/src/components/Input/index.d.ts +2 -0
  16. package/dist/admin/src/components/PluginIcon/index.d.ts +2 -0
  17. package/dist/admin/src/components/SidePanel/SidePanel.d.ts +7 -0
  18. package/dist/admin/src/helpers/content.d.ts +5 -0
  19. package/dist/admin/src/helpers/storage.d.ts +15 -0
  20. package/dist/admin/src/hooks/useSearchedEntries.d.ts +6 -0
  21. package/dist/admin/src/hooks/useTranslate.d.ts +4 -0
  22. package/dist/admin/src/index.d.ts +9 -0
  23. package/dist/admin/src/interface.d.ts +33 -0
  24. package/dist/admin/src/pluginId.d.ts +2 -0
  25. package/dist/server/index.js +506 -25
  26. package/dist/server/index.mjs +509 -0
  27. package/dist/server/src/bootstrap.d.ts +5 -0
  28. package/dist/server/src/config/index.d.ts +13 -0
  29. package/dist/server/src/content-types/index.d.ts +35 -0
  30. package/dist/server/src/content-types/mctr-relation/index.d.ts +33 -0
  31. package/dist/server/src/content-types/mctr-relation/schema.d.ts +31 -0
  32. package/dist/server/src/controllers/controller.d.ts +27 -0
  33. package/dist/server/src/controllers/index.d.ts +28 -0
  34. package/dist/server/src/destroy.d.ts +5 -0
  35. package/dist/server/src/helpers/index.d.ts +2 -0
  36. package/dist/server/src/index.d.ts +113 -0
  37. package/dist/server/src/interface.d.ts +42 -0
  38. package/dist/server/src/middlewares/index.d.ts +4 -0
  39. package/dist/server/src/middlewares/middleware.d.ts +2 -0
  40. package/dist/server/src/policies/index.d.ts +2 -0
  41. package/dist/server/src/register.d.ts +5 -0
  42. package/dist/server/src/routes/index.d.ts +18 -0
  43. package/dist/server/src/services/index.d.ts +8 -0
  44. package/dist/server/src/services/service.d.ts +7 -0
  45. package/dist/server/src/utils.d.ts +3 -0
  46. package/package.json +55 -27
  47. package/TODO.md +0 -4
  48. package/admin/src/components/Input/InputContentSuggestions.tsx +0 -162
  49. package/admin/src/components/Input/MainInput.tsx +0 -135
  50. package/admin/src/components/Input/PublicationState.tsx +0 -28
  51. package/admin/src/components/Input/TableItem.tsx +0 -109
  52. package/admin/src/components/Input/index.tsx +0 -27
  53. package/admin/src/components/PluginIcon/index.tsx +0 -12
  54. package/admin/src/helpers/content.ts +0 -60
  55. package/admin/src/helpers/storage.ts +0 -32
  56. package/admin/src/hooks/useSearchedEntries.ts +0 -41
  57. package/admin/src/index.tsx +0 -140
  58. package/admin/src/interface.ts +0 -37
  59. package/admin/src/pluginId.ts +0 -5
  60. package/admin/src/translations/en.json +0 -1
  61. package/admin/src/translations/fr.json +0 -1
  62. package/admin/src/utils/getTrad.ts +0 -5
  63. package/dist/server/bootstrap.js +0 -5
  64. package/dist/server/config/index.js +0 -27
  65. package/dist/server/content-types/index.js +0 -3
  66. package/dist/server/controllers/controller.js +0 -92
  67. package/dist/server/controllers/index.js +0 -9
  68. package/dist/server/destroy.js +0 -5
  69. package/dist/server/interface.js +0 -2
  70. package/dist/server/middlewares/index.js +0 -9
  71. package/dist/server/middlewares/middleware.js +0 -163
  72. package/dist/server/policies/index.js +0 -3
  73. package/dist/server/register.js +0 -15
  74. package/dist/server/routes/index.js +0 -29
  75. package/dist/server/services/index.js +0 -9
  76. package/dist/server/services/service.js +0 -8
  77. package/dist/server/utils.js +0 -15
  78. package/dist/tsconfig.server.tsbuildinfo +0 -1
  79. package/server/bootstrap.ts +0 -5
  80. package/server/config/index.ts +0 -28
  81. package/server/content-types/index.ts +0 -1
  82. package/server/controllers/controller.ts +0 -107
  83. package/server/controllers/index.ts +0 -5
  84. package/server/destroy.ts +0 -5
  85. package/server/index.ts +0 -23
  86. package/server/interface.ts +0 -50
  87. package/server/middlewares/index.ts +0 -5
  88. package/server/middlewares/middleware.ts +0 -197
  89. package/server/policies/index.ts +0 -1
  90. package/server/register.ts +0 -14
  91. package/server/routes/index.ts +0 -27
  92. package/server/services/index.ts +0 -5
  93. package/server/services/service.ts +0 -11
  94. package/server/utils.ts +0 -14
  95. package/strapi-admin.js +0 -3
  96. package/strapi-server.js +0 -3
  97. package/tsconfig.json +0 -20
  98. 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,4 @@
1
+ declare const _default: {
2
+ middleware: (ctx: any, next: any) => Promise<void>;
3
+ };
4
+ export default _default;
@@ -0,0 +1,2 @@
1
+ declare const _default: (ctx: any, next: any) => Promise<void>;
2
+ export default _default;
@@ -0,0 +1,2 @@
1
+ declare const _default: {};
2
+ export default _default;
@@ -0,0 +1,5 @@
1
+ import type { Core } from '@strapi/strapi';
2
+ declare const register: ({ strapi }: {
3
+ strapi: Core.Strapi;
4
+ }) => void;
5
+ export default register;
@@ -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;
@@ -0,0 +1,8 @@
1
+ declare const _default: {
2
+ service: ({ strapi }: {
3
+ strapi: import("@strapi/types/dist/core").Strapi;
4
+ }) => {
5
+ getFirstStringFieldInContentType(contentType: any): string;
6
+ };
7
+ };
8
+ export default _default;
@@ -0,0 +1,7 @@
1
+ import type { Core } from '@strapi/strapi';
2
+ declare const _default: ({ strapi }: {
3
+ strapi: Core.Strapi;
4
+ }) => {
5
+ getFirstStringFieldInContentType(contentType: any): string;
6
+ };
7
+ export default _default;
@@ -0,0 +1,3 @@
1
+ import { Configuration } from './interface';
2
+ export declare const getPluginConfiguration: () => Configuration;
3
+ export declare const log: (message: string) => void;
package/package.json CHANGED
@@ -1,53 +1,81 @@
1
1
  {
2
2
  "name": "multi-content-type-relation",
3
- "version": "0.1.0",
4
- "description": "This is the description of the plugin.",
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": "A Strapi developer"
7
+ "name": "bleulliette@kaliop.com"
8
8
  },
9
9
  "maintainers": [
10
10
  {
11
- "name": "A Strapi developer"
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": "tsc -p tsconfig.server.json",
16
- "develop": "tsc -p tsconfig.server.json -w"
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": "^1.6.3",
21
- "@strapi/helper-plugin": "^4.6.0",
22
- "@strapi/icons": "^1.6.3",
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/typescript-utils": "^4.6.0",
27
- "@types/react": "^17.0.53",
28
- "@types/react-dom": "^18.0.28",
29
- "@types/react-router-dom": "^5.3.3",
30
- "@types/styled-components": "^5.1.26",
31
- "react": "^18.2.0",
32
- "react-dom": "^18.2.0",
33
- "react-router-dom": "^5.3.4",
34
- "styled-components": "^5.3.6",
35
- "typescript": "^5.0.4"
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/strapi": "^4.13.3",
39
- "react": "^17.0.0 || ^18.0.0",
40
- "react-dom": "^17.0.0 || ^18.0.0",
41
- "react-router-dom": "^5.3.4",
42
- "styled-components": "^5.3.3"
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": ">=16.0.0 <=20.x.x",
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": "Description of multi-content-type-relation plugin",
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,4 +0,0 @@
1
- # Remaining tasks
2
-
3
- - publish it
4
- - Create tests
@@ -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
- }