@sensinum/strapi-plugin-multi-domain 5.1.1
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/LICENSE.md +7 -0
- package/README.md +250 -0
- package/dist/_chunks/QueryProvider-DLzv0qqi.mjs +20 -0
- package/dist/_chunks/QueryProvider-DUoM4JhC.js +1 -0
- package/dist/_chunks/Settings-BPM919an.mjs +23 -0
- package/dist/_chunks/Settings-CT4xmD8_.js +1 -0
- package/dist/_chunks/TenantCustomField-BYDxTP5y.js +1 -0
- package/dist/_chunks/TenantCustomField-CtCNZEgB.mjs +93 -0
- package/dist/_chunks/TenantEdit-BdSUNboL.mjs +494 -0
- package/dist/_chunks/TenantEdit-C4LHKCAr.js +1 -0
- package/dist/_chunks/TenantList-Cl9445sU.js +1 -0
- package/dist/_chunks/TenantList-DWA0Z3-T.mjs +272 -0
- package/dist/_chunks/en-BIUQvQi4.mjs +104 -0
- package/dist/_chunks/en-Dj_hyppO.js +1 -0
- package/dist/_chunks/index-Ck1YIRr4.js +1 -0
- package/dist/_chunks/index-D9I-QR8y.js +1 -0
- package/dist/_chunks/index-DkEjJa1n.mjs +182 -0
- package/dist/_chunks/index-Ds2z-yoa.mjs +315 -0
- package/dist/_chunks/useTenants-CE8J0EOu.mjs +93 -0
- package/dist/_chunks/useTenants-XMURO_eU.js +1 -0
- package/dist/admin/index.js +1 -0
- package/dist/admin/index.mjs +4 -0
- package/dist/server/index.js +4 -0
- package/dist/server/index.mjs +6249 -0
- package/dist/shared/config.d.ts +7 -0
- package/dist/shared/config.d.ts.map +1 -0
- package/dist/shared/permissions.d.ts +35 -0
- package/dist/shared/permissions.d.ts.map +1 -0
- package/dist/shared/pluginIds.d.ts +9 -0
- package/dist/shared/pluginIds.d.ts.map +1 -0
- package/dist/shared/schemas/admin.d.ts +26 -0
- package/dist/shared/schemas/admin.d.ts.map +1 -0
- package/dist/shared/schemas/index.d.ts +8 -0
- package/dist/shared/schemas/index.d.ts.map +1 -0
- package/dist/shared/schemas/permision.schema.d.ts +28 -0
- package/dist/shared/schemas/permision.schema.d.ts.map +1 -0
- package/dist/shared/schemas/query-params.schema.d.ts +19 -0
- package/dist/shared/schemas/query-params.schema.d.ts.map +1 -0
- package/dist/shared/schemas/role.schema.d.ts +101 -0
- package/dist/shared/schemas/role.schema.d.ts.map +1 -0
- package/dist/shared/schemas/tenant.schema.d.ts +428 -0
- package/dist/shared/schemas/tenant.schema.d.ts.map +1 -0
- package/dist/shared/schemas/token.schema.d.ts +13 -0
- package/dist/shared/schemas/token.schema.d.ts.map +1 -0
- package/dist/shared/schemas/uniq-validation.schema.d.ts +18 -0
- package/dist/shared/schemas/uniq-validation.schema.d.ts.map +1 -0
- package/dist/shared/schemas/utils.d.ts +3 -0
- package/dist/shared/schemas/utils.d.ts.map +1 -0
- package/package.json +116 -0
package/LICENSE.md
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
<div align="center" width="150px">
|
|
2
|
+
<img style="width: 150px; height: auto;" src="https://www.sensinum.com/img/open-source/strapi-plugin-multi-domain/logo.png" alt="Logo - Strapi Plugin Multi-Domain" />
|
|
3
|
+
</div>
|
|
4
|
+
<div align="center">
|
|
5
|
+
<h1>Strapi - Multi-Domain Plugin</h1>
|
|
6
|
+
<p>The Strength of Many, Managed as One</p>
|
|
7
|
+
<a href="https://www.npmjs.org/package/@sensinum/strapi-plugin-multi-domain">
|
|
8
|
+
<img alt="GitHub package.json version" src="https://img.shields.io/npm/v/@sensinum/strapi-plugin-multi-domain">
|
|
9
|
+
</a>
|
|
10
|
+
<a href="https://www.npmjs.org/package/@sensinum/strapi-plugin-multi-domain">
|
|
11
|
+
<img src="https://img.shields.io/npm/dm/@sensinum/strapi-plugin-multi-domain.svg" alt="Monthly download on NPM" />
|
|
12
|
+
</a>
|
|
13
|
+
<a href="https://circleci.com/gh/VirtusLab/strapi-plugin-multi-tenant">
|
|
14
|
+
<img src="https://circleci.com/gh/VirtusLab/strapi-plugin-multi-tenant.svg?style=shield&circle-token=CCIPRJ_GvBYVgBPwwYgrsccTd71zn_3c329af4be4c2f74b2bad15d4e1336ac67c548e6" alt="CircleCI" />
|
|
15
|
+
</a>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
This plugin brings multi-domain support to your Strapi application. It allows you to isolate data between different domains, providing a robust solution for SaaS applications and other multi-user platforms where data separation is crucial.
|
|
21
|
+
|
|
22
|
+
> โ
**GET THE LICENSE**: To obtain the license we encourage you to [**visit the plugin commercial website**](http://sensinum.com/strapi-multi-domain-plugin), select the appropriate plan, and reach out to your preferred reseller or directly contact the plugin authors via [**this form**](http://sensinum.com/contact)
|
|
23
|
+
|
|
24
|
+
> โ ๏ธ **This plugin is licensed by VirtusLab Ltd. under the [End User License Agreement (EULA)](https://sensinum.com/eula-strapi-multi-domain-plugin).**
|
|
25
|
+
Unauthorized use, including usage without a valid license key or modification of the code in a way that breaches the integrity of the software, is strictly prohibited and will be treated as a violation of the [EULA](https://sensinum.com/eula-strapi-multi-domain-plugin), with all associated legal consequences.
|
|
26
|
+
|
|
27
|
+
### Table of Contents
|
|
28
|
+
|
|
29
|
+
1. [โจ Features](#-features)
|
|
30
|
+
2. [๐จ Admin Panel Features](#-admin-panel-features)
|
|
31
|
+
3. [โ๏ธ How it works](#%EF%B8%8F-how-it-works)
|
|
32
|
+
4. [โ ๏ธ Important Considerations](#%EF%B8%8F-important-considerations)
|
|
33
|
+
5. [๐ง Configuration](#-configuration)
|
|
34
|
+
6. [๐งช Testing](#-testing)
|
|
35
|
+
7. [๐จโ๐ป For Developers: Advanced Customization](#-for-developers-advanced-customization-via-sharedpluginidsts)
|
|
36
|
+
8. [๐ License](#-license)
|
|
37
|
+
|
|
38
|
+
## โจ Features
|
|
39
|
+
|
|
40
|
+
- **Domain-based Data Isolation**: Automatically scopes content types to the current user's domain.
|
|
41
|
+
- **Media Library Segregation**: Each domain has its own isolated section within the media library.
|
|
42
|
+
- **User-Domain Association**: Users can be associated with one or more domains through Strapi roles.
|
|
43
|
+
- **Domain Selector**: A convenient domain selector is added to the admin UI for users who belong to multiple domains.
|
|
44
|
+
- **Super Admin Access**: Super admins have the ability to view data across all domains.
|
|
45
|
+
- **Configurable**: Easily configure which content types should be domain-aware.
|
|
46
|
+
|
|
47
|
+
## ๐จ Admin Panel Features
|
|
48
|
+
|
|
49
|
+
- **Domain Management UI**: A dedicated settings page in the admin panel (`Settings > Multi-Domain Settings`) allows administrators to create, view, update, and delete domains.
|
|
50
|
+
- **Domain Selector**: Users belonging to multiple domains will see a "Select domain" option in the user menu (top right). This opens a modal allowing them to easily switch between their assigned domains, reloading the app context for the selected domain.
|
|
51
|
+
- **"Unique by Domain" Custom Field**: The plugin registers a new custom field type named "Unique by domain". When applied to a text field on a content type, it ensures that the value of that field is unique among all entries of that type within the *same domain*.
|
|
52
|
+
- **Content Manager Integration**: To prevent content from being created without a domain, the "Create new entry" button in the Content Manager is hidden when the "Super Admin" domain is selected.
|
|
53
|
+
|
|
54
|
+
## โ๏ธ How it works
|
|
55
|
+
|
|
56
|
+
The plugin introduces a `Domain` collection type and associates other content types with it. When a user logs in, they are associated with a domain. All their actions within the Content Manager and Media Library are then scoped to that domain. This is achieved by decorating Strapi's core services and controllers to automatically add domain filters to database queries.
|
|
57
|
+
|
|
58
|
+
## โ ๏ธ Important Considerations
|
|
59
|
+
|
|
60
|
+
### Users & Permissions Plugin Roles
|
|
61
|
+
|
|
62
|
+
When using the **Users & Permissions plugin** with multi-domain functionality, there's an important limitation to be aware of:
|
|
63
|
+
|
|
64
|
+
**The `public` role does not work with multi-domain by default** because the plugin cannot recognize which domain the request belongs to without proper authentication context.
|
|
65
|
+
|
|
66
|
+
#### Solutions:
|
|
67
|
+
|
|
68
|
+
1. **Add `auth: false` to routes**: For routes that need to be publicly accessible across domains, you can disable authentication by adding `auth: false` to the route configuration.
|
|
69
|
+
|
|
70
|
+
2. **Use API tokens assigned to domains**: Ensure all requests include an API token that is properly assigned to a specific domain. This allows the plugin to identify the correct domain context for the request.
|
|
71
|
+
|
|
72
|
+
#### Example:
|
|
73
|
+
```javascript
|
|
74
|
+
// In your route configuration
|
|
75
|
+
module.exports = {
|
|
76
|
+
routes: [
|
|
77
|
+
{
|
|
78
|
+
method: 'GET',
|
|
79
|
+
path: '/my-public-endpoint',
|
|
80
|
+
handler: 'my-controller.myPublicMethod',
|
|
81
|
+
config: {
|
|
82
|
+
auth: false, // Disables authentication for this route
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
};
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Without these configurations, public requests may fail or return unexpected results because the multi-domain plugin cannot determine the appropriate domain context.
|
|
90
|
+
|
|
91
|
+
## ๐ง Configuration
|
|
92
|
+
|
|
93
|
+
To configure the plugin, create a `plugins.js` (or `plugins.ts`) file in your Strapi project's `config` directory.
|
|
94
|
+
|
|
95
|
+
File: `config/plugins.js`
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
module.exports = {
|
|
99
|
+
'multi-domain': {
|
|
100
|
+
enabled: true,
|
|
101
|
+
config: {
|
|
102
|
+
/**
|
|
103
|
+
* The code of default domain is used to create a domain for super admins.
|
|
104
|
+
* By default, it uses the super admin role code from Strapi.
|
|
105
|
+
*/
|
|
106
|
+
defaultDomain: {
|
|
107
|
+
name: 'Super Admin',
|
|
108
|
+
// code is an unique identifier for the domain
|
|
109
|
+
// by default it's 'strapi-super-admin'
|
|
110
|
+
customFields: {},
|
|
111
|
+
},
|
|
112
|
+
/**
|
|
113
|
+
* A set of content-type uids that should not be scoped by domain.
|
|
114
|
+
* By default, all content-types are scoped except for some internal Strapi models.
|
|
115
|
+
* @returns {Set<string>}
|
|
116
|
+
*/
|
|
117
|
+
getOmitContentTypes: () => new Set([
|
|
118
|
+
'api::my-global-content-type.my-global-content-type',
|
|
119
|
+
]),
|
|
120
|
+
/**
|
|
121
|
+
* A list of content-api endpoints to exclude from domain logic.
|
|
122
|
+
* e.g., ['/api/my-public-endpoint']
|
|
123
|
+
* @returns {string[]}
|
|
124
|
+
*/
|
|
125
|
+
getExcludedContentAPIEndpoints: () => [],
|
|
126
|
+
getExtraProxyDecorators: () => [
|
|
127
|
+
{
|
|
128
|
+
condition: (modelName) => modelName === 'api::article.article',
|
|
129
|
+
handler(target, targetElement, ctx, prop, modelName) {
|
|
130
|
+
// This handler will apply only to the 'article' content type
|
|
131
|
+
return async (queryParams, ...restParams) => {
|
|
132
|
+
const newQueryParams = {
|
|
133
|
+
...queryParams,
|
|
134
|
+
where: {
|
|
135
|
+
...(queryParams?.where || {}),
|
|
136
|
+
// Add some custom filter based on domain or user logic
|
|
137
|
+
customFilter: true,
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
// Call the original database method with modified params
|
|
141
|
+
return targetElement(newQueryParams, ...restParams);
|
|
142
|
+
};
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Configuration Options
|
|
152
|
+
|
|
153
|
+
- `defaultDomain` (object): Configures the domain that is automatically created for super admins.
|
|
154
|
+
- `name` (string): The display name for the default domain.
|
|
155
|
+
- `customFields` (object): A JSON object for any custom data you want to associate with the domain.
|
|
156
|
+
- `getOmitContentTypes` (function): Returns a `Set` of content-type UIDs that should be excluded from multi-domain. Use this for global, non-domain-specific content.
|
|
157
|
+
- `getExcludedContentAPIEndpoints` (function): Returns an array of URL paths for Content API endpoints that should be publicly accessible and not scoped by domain.
|
|
158
|
+
- `getExtraProxyDecorators` (function): An advanced feature that allows you to add custom logic to the database query proxy. This is useful for implementing complex, domain-specific business rules. It should return an array of objects, each with a `condition` and a `handler` function.
|
|
159
|
+
- `condition` (function): Takes a `modelName` and returns `true` if the handler should be applied.
|
|
160
|
+
- `handler` (function): A function that wraps the original database method, allowing you to modify its behavior. It receives the `target` repository, the original `targetElement` method, the Koa `ctx`, the method `prop` name, and the `modelName`.
|
|
161
|
+
|
|
162
|
+
Here's an example of how you might use `getExtraProxyDecorators` to add an additional filter to a specific content type:
|
|
163
|
+
|
|
164
|
+
```javascript
|
|
165
|
+
// config/plugins.js
|
|
166
|
+
module.exports = {
|
|
167
|
+
'multi-domain': {
|
|
168
|
+
enabled: true,
|
|
169
|
+
config: {
|
|
170
|
+
/**
|
|
171
|
+
* The code of default domain is used to create a domain for super admins.
|
|
172
|
+
* By default, it uses the super admin role code from Strapi.
|
|
173
|
+
*/
|
|
174
|
+
defaultDomain: {
|
|
175
|
+
name: 'Super Admin',
|
|
176
|
+
// code is an unique identifier for the domain
|
|
177
|
+
// by default it's 'strapi-super-admin'
|
|
178
|
+
customFields: {},
|
|
179
|
+
},
|
|
180
|
+
/**
|
|
181
|
+
* A set of content-type uids that should not be scoped by domain.
|
|
182
|
+
* By default, all content-types are scoped except for some internal Strapi models.
|
|
183
|
+
* @returns {Set<string>}
|
|
184
|
+
*/
|
|
185
|
+
getOmitContentTypes: () => new Set([
|
|
186
|
+
'api::my-global-content-type.my-global-content-type',
|
|
187
|
+
]),
|
|
188
|
+
/**
|
|
189
|
+
* A list of content-api endpoints to exclude from domain logic.
|
|
190
|
+
* e.g., ['/api/my-public-endpoint']
|
|
191
|
+
* @returns {string[]}
|
|
192
|
+
*/
|
|
193
|
+
getExcludedContentAPIEndpoints: () => [],
|
|
194
|
+
getExtraProxyDecorators: () => [
|
|
195
|
+
{
|
|
196
|
+
condition: (modelName) => modelName === 'api::article.article',
|
|
197
|
+
handler(target, targetElement, ctx, prop, modelName) {
|
|
198
|
+
// This handler will apply only to the 'article' content type
|
|
199
|
+
return async (queryParams, ...restParams) => {
|
|
200
|
+
const newQueryParams = {
|
|
201
|
+
...queryParams,
|
|
202
|
+
where: {
|
|
203
|
+
...(queryParams?.where || {}),
|
|
204
|
+
// Add some custom filter based on domain or user logic
|
|
205
|
+
customFilter: true,
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
// Call the original database method with modified params
|
|
209
|
+
return targetElement(newQueryParams, ...restParams);
|
|
210
|
+
};
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## ๐งช Testing
|
|
220
|
+
|
|
221
|
+
To run the tests for this plugin, navigate to the plugin's directory and run the following command:
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
This will execute the Jest test suite. To run tests in watch mode, you can use `yarn test:watch`.
|
|
225
|
+
|
|
226
|
+
## ๐จโ๐ป For Developers: Advanced Customization via `shared/pluginIds.ts`
|
|
227
|
+
|
|
228
|
+
The `shared/pluginIds.ts` file is a centralized module for constants that are used across both the server-side and client-side parts of the plugin. While it's not part of the standard configuration, it offers a powerful way for developers to perform advanced customizations or re-brand the plugin's core concepts.
|
|
229
|
+
|
|
230
|
+
**Warning:** Modifying these values is an advanced procedure. It can have widespread effects and potentially break the plugin's functionality if not done carefully and consistently.
|
|
231
|
+
|
|
232
|
+
### What can be achieved by changing this file?
|
|
233
|
+
|
|
234
|
+
By changing the values in this file, you can fundamentally alter the plugin's identifiers. For example, if your business logic uses the term "Brand" instead of "Domain," you could update `modelId` to `'brand'` to make the content type, API routes, and database relationships align with your terminology.
|
|
235
|
+
|
|
236
|
+
Here's a breakdown of the constants and what you can control by changing them:
|
|
237
|
+
|
|
238
|
+
- `pluginId`: The official ID of the plugin (`multi-domain`). Changing this will alter the plugin's root path in Strapi, including settings URLs and permission domains.
|
|
239
|
+
|
|
240
|
+
> **Note:** This value is derived from the `strapi.name` property in `package.json` and should always be kept in sync with it.
|
|
241
|
+
|
|
242
|
+
- `modelId`: The singular ID for the domain content type (default: `'domain'`). Changing this renames the content type itself, which affects the database table, API endpoints, and the name of the relationship field added to other content types.
|
|
243
|
+
- `modelServiceId`: The identifier for the domain service (default: `'domain'`). This should typically be kept in sync with `modelId`.
|
|
244
|
+
- `userField`: The property name on the `user` object where associated domain information is stored (default: `'domain'`).
|
|
245
|
+
- `modelRoute`: The base route for the domain management API (default: `'domains'`). This is derived from `modelId`, so changing `modelId` will automatically update this.
|
|
246
|
+
- `COOKIE_NAME`: The name of the cookie used to store the user's currently selected domain (default: `'domain'`). You might change this to avoid naming conflicts with other cookies in your application.
|
|
247
|
+
|
|
248
|
+
## ๐ License
|
|
249
|
+
|
|
250
|
+
All rights reserved. Copyright (c) [VirtusLab Ltd](https://virtuslab.com/).
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as r } from "react/jsx-runtime";
|
|
2
|
+
import { QueryClientProvider as t, QueryClient as i } from "@tanstack/react-query";
|
|
3
|
+
const o = new i({
|
|
4
|
+
defaultOptions: {
|
|
5
|
+
queries: {
|
|
6
|
+
retry: !1,
|
|
7
|
+
refetchOnWindowFocus: !1,
|
|
8
|
+
staleTime: 5 * 60 * 1e3,
|
|
9
|
+
// 5 minutes
|
|
10
|
+
gcTime: 10 * 60 * 1e3
|
|
11
|
+
// 10 minutes (formerly cacheTime)
|
|
12
|
+
},
|
|
13
|
+
mutations: {
|
|
14
|
+
retry: !1
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}), l = ({ children: e }) => /* @__PURE__ */ r(t, { client: o, children: e });
|
|
18
|
+
export {
|
|
19
|
+
l as Q
|
|
20
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const t=require("react/jsx-runtime"),e=require("@tanstack/react-query"),i=new e.QueryClient({defaultOptions:{queries:{retry:!1,refetchOnWindowFocus:!1,staleTime:5*60*1e3,gcTime:10*60*1e3},mutations:{retry:!1}}}),s=({children:r})=>t.jsx(e.QueryClientProvider,{client:i,children:r});exports.QueryProvider=s;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as e, jsxs as i } from "react/jsx-runtime";
|
|
2
|
+
import { lightTheme as a, darkTheme as n, DesignSystemProvider as s } from "@strapi/design-system";
|
|
3
|
+
import o from "react";
|
|
4
|
+
import { Routes as l, Route as t, Navigate as p } from "react-router-dom";
|
|
5
|
+
import { m as r, p as d } from "./index-Ds2z-yoa.mjs";
|
|
6
|
+
import { Q as h } from "./QueryProvider-DLzv0qqi.mjs";
|
|
7
|
+
const c = o.lazy(() => import("./TenantList-DWA0Z3-T.mjs")), g = o.lazy(() => import("./TenantEdit-BdSUNboL.mjs")), P = () => {
|
|
8
|
+
const m = localStorage.getItem("STRAPI_THEME") === "light" ? a : n;
|
|
9
|
+
return /* @__PURE__ */ e(s, { theme: m, children: /* @__PURE__ */ e(h, { children: /* @__PURE__ */ i(l, { children: [
|
|
10
|
+
/* @__PURE__ */ e(t, { path: r, element: /* @__PURE__ */ e(c, {}) }),
|
|
11
|
+
/* @__PURE__ */ e(t, { path: `/${r}/:id`, element: /* @__PURE__ */ e(g, {}) }),
|
|
12
|
+
/* @__PURE__ */ e(
|
|
13
|
+
t,
|
|
14
|
+
{
|
|
15
|
+
path: "*",
|
|
16
|
+
element: /* @__PURE__ */ e(p, { to: `/settings/${d}/${r}`, replace: !0 })
|
|
17
|
+
}
|
|
18
|
+
)
|
|
19
|
+
] }) }) });
|
|
20
|
+
};
|
|
21
|
+
export {
|
|
22
|
+
P as default
|
|
23
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),s=require("@strapi/design-system"),i=require("react"),r=require("react-router-dom"),o=require("./index-Ck1YIRr4.js"),u=require("./QueryProvider-DUoM4JhC.js"),l=t=>t&&t.__esModule?t:{default:t},n=l(i),a=n.default.lazy(()=>Promise.resolve().then(()=>require("./TenantList-Cl9445sU.js"))),d=n.default.lazy(()=>Promise.resolve().then(()=>require("./TenantEdit-C4LHKCAr.js"))),c=()=>{const t=localStorage.getItem("STRAPI_THEME")==="light"?s.lightTheme:s.darkTheme;return e.jsx(s.DesignSystemProvider,{theme:t,children:e.jsx(u.QueryProvider,{children:e.jsxs(r.Routes,{children:[e.jsx(r.Route,{path:o.modelRoute,element:e.jsx(a,{})}),e.jsx(r.Route,{path:`/${o.modelRoute}/:id`,element:e.jsx(d,{})}),e.jsx(r.Route,{path:"*",element:e.jsx(r.Navigate,{to:`/settings/${o.pluginId}/${o.modelRoute}`,replace:!0})})]})})})};exports.default=c;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=require("react/jsx-runtime"),a=require("@strapi/design-system"),z=require("@strapi/strapi/admin"),e=require("zod"),F=require("react"),S=require("@tanstack/react-query"),q=require("./index-D9I-QR8y.js"),p=require("./QueryProvider-DUoM4JhC.js"),C=require("remeda"),M=t=>t&&t.__esModule?t:{default:t},_=M(F),g=e.z.object({modified:e.z.boolean(),isSubmitting:e.z.boolean(),disabled:e.z.boolean(),values:e.z.record(e.z.any()),initialValues:e.z.record(e.z.any()),errors:e.z.record(e.z.any()),onChange:e.z.function().args(e.z.object({target:e.z.object({name:e.z.string(),value:e.z.any()})})).returns(e.z.void()),resetForm:e.z.function().returns(e.z.void())}),P=t=>{const r=z.unstable_useContentManagerContext(),{form:o}=r,u=t?g.merge(t):g;return _.default.useMemo(()=>({...r,form:u.passthrough().parse(o)}),[r,o,u])},R=()=>S.useMutation({mutationFn:async t=>q.api.validateCustomField(t)}),T=e.z.object({setSubmitting:e.z.function().args(e.z.boolean()).returns(e.z.void())}),Q=({disabled:t,placeholder:r,required:o,hint:u,value:f,onChange:c,label:b,name:i,rawError:l})=>{const{id:y,model:v,form:d}=P(T),m=R(),{toggleNotification:j}=z.useNotification(),x=async h=>{try{d.setSubmitting(!0);const s=await m.mutateAsync({model:v,field:i,documentId:y,value:h.target.value});c({target:{value:s.value,name:i}})}catch(s){j({type:"danger",message:s instanceof Error?s.message:s.toString()})}finally{d.setSubmitting(!1)}};return n.jsxs(a.Field.Root,{name:i,hint:m.isPending?"Validating...":u,required:o,error:l?.defaultMessage,children:[n.jsx(a.Field.Label,{children:C.capitalize(b)}),n.jsx(a.TextInput,{name:i,value:f,onChange:c,onBlur:x,placeholder:r,disabled:t,error:!!l,"aria-describedby":`${i}-hint`,"aria-required":o}),n.jsx(a.Field.Hint,{}),n.jsx(a.Field.Error,{})]})},V=t=>n.jsx(p.QueryProvider,{children:n.jsx(Q,{...t})});exports.default=V;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { jsx as o, jsxs as F } from "react/jsx-runtime";
|
|
2
|
+
import { Field as u, TextInput as S } from "@strapi/design-system";
|
|
3
|
+
import { unstable_useContentManagerContext as x, useNotification as j } from "@strapi/strapi/admin";
|
|
4
|
+
import { z as t } from "zod";
|
|
5
|
+
import M from "react";
|
|
6
|
+
import { useMutation as P } from "@tanstack/react-query";
|
|
7
|
+
import { a as T } from "./index-DkEjJa1n.mjs";
|
|
8
|
+
import { Q as V } from "./QueryProvider-DLzv0qqi.mjs";
|
|
9
|
+
import { capitalize as z } from "remeda";
|
|
10
|
+
const f = t.object({
|
|
11
|
+
modified: t.boolean(),
|
|
12
|
+
isSubmitting: t.boolean(),
|
|
13
|
+
disabled: t.boolean(),
|
|
14
|
+
values: t.record(t.any()),
|
|
15
|
+
initialValues: t.record(t.any()),
|
|
16
|
+
errors: t.record(t.any()),
|
|
17
|
+
onChange: t.function().args(t.object({ target: t.object({ name: t.string(), value: t.any() }) })).returns(t.void()),
|
|
18
|
+
resetForm: t.function().returns(t.void())
|
|
19
|
+
}), B = (e) => {
|
|
20
|
+
const r = x(), { form: n } = r, s = e ? f.merge(e) : f;
|
|
21
|
+
return M.useMemo(
|
|
22
|
+
() => ({
|
|
23
|
+
...r,
|
|
24
|
+
form: s.passthrough().parse(n)
|
|
25
|
+
}),
|
|
26
|
+
[r, n, s]
|
|
27
|
+
);
|
|
28
|
+
}, I = () => P({
|
|
29
|
+
mutationFn: async (e) => T.validateCustomField(e)
|
|
30
|
+
}), N = t.object({
|
|
31
|
+
setSubmitting: t.function().args(t.boolean()).returns(t.void())
|
|
32
|
+
}), Q = ({
|
|
33
|
+
disabled: e,
|
|
34
|
+
placeholder: r,
|
|
35
|
+
required: n,
|
|
36
|
+
hint: s,
|
|
37
|
+
value: g,
|
|
38
|
+
onChange: c,
|
|
39
|
+
label: b,
|
|
40
|
+
name: i,
|
|
41
|
+
rawError: l
|
|
42
|
+
}) => {
|
|
43
|
+
const { id: p, model: v, form: m } = B(N), d = I(), { toggleNotification: y } = j(), h = async (C) => {
|
|
44
|
+
try {
|
|
45
|
+
m.setSubmitting(!0);
|
|
46
|
+
const a = await d.mutateAsync({
|
|
47
|
+
model: v,
|
|
48
|
+
field: i,
|
|
49
|
+
documentId: p,
|
|
50
|
+
value: C.target.value
|
|
51
|
+
});
|
|
52
|
+
c({ target: { value: a.value, name: i } });
|
|
53
|
+
} catch (a) {
|
|
54
|
+
y({
|
|
55
|
+
type: "danger",
|
|
56
|
+
message: a instanceof Error ? a.message : a.toString()
|
|
57
|
+
});
|
|
58
|
+
} finally {
|
|
59
|
+
m.setSubmitting(!1);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
return /* @__PURE__ */ F(
|
|
63
|
+
u.Root,
|
|
64
|
+
{
|
|
65
|
+
name: i,
|
|
66
|
+
hint: d.isPending ? "Validating..." : s,
|
|
67
|
+
required: n,
|
|
68
|
+
error: l?.defaultMessage,
|
|
69
|
+
children: [
|
|
70
|
+
/* @__PURE__ */ o(u.Label, { children: z(b) }),
|
|
71
|
+
/* @__PURE__ */ o(
|
|
72
|
+
S,
|
|
73
|
+
{
|
|
74
|
+
name: i,
|
|
75
|
+
value: g,
|
|
76
|
+
onChange: c,
|
|
77
|
+
onBlur: h,
|
|
78
|
+
placeholder: r,
|
|
79
|
+
disabled: e,
|
|
80
|
+
error: !!l,
|
|
81
|
+
"aria-describedby": `${i}-hint`,
|
|
82
|
+
"aria-required": n
|
|
83
|
+
}
|
|
84
|
+
),
|
|
85
|
+
/* @__PURE__ */ o(u.Hint, {}),
|
|
86
|
+
/* @__PURE__ */ o(u.Error, {})
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
}, w = (e) => /* @__PURE__ */ o(V, { children: /* @__PURE__ */ o(Q, { ...e }) });
|
|
91
|
+
export {
|
|
92
|
+
w as default
|
|
93
|
+
};
|