commit
4f0ceccb85
|
@ -41,9 +41,13 @@ jobs:
|
||||||
node-version: ${{ matrix.node }}
|
node-version: ${{ matrix.node }}
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
- name: Install dependencies plugin
|
- name: Install dependencies plugin
|
||||||
run: yarn --no-lockfile --unsafe-perm --production
|
run: yarn --no-lockfile --unsafe-perm
|
||||||
|
- name: Push the package to yalc
|
||||||
|
run: yarn build
|
||||||
|
- name: Add yalc package to the playground
|
||||||
|
run: yarn playground:yalc-add
|
||||||
- name: Install dependencies playground
|
- name: Install dependencies playground
|
||||||
run: yarn playground:install --unsafe-perm
|
run: cd playground && yarn install --unsafe-perm
|
||||||
- name: Build playground
|
- name: Build playground
|
||||||
run: yarn playground:build
|
run: yarn playground:build
|
||||||
- name: Run test
|
- name: Run test
|
||||||
|
|
|
@ -4,60 +4,55 @@ We want this community to be friendly and respectful to each other. Please follo
|
||||||
|
|
||||||
## Development Workflow
|
## Development Workflow
|
||||||
|
|
||||||
To get started with the project, make sure you have a local instance of Strapi running.
|
This plugin provides a local development instance of Strapi to develop it's features. We call this instance `playground` and it can be found in the playground folder in the root of the project. For that reason it is not needed to have your own Strapi instance running to work on this plugin. Just clone the repo and you're ready to go!
|
||||||
See the [Strapi docs](https://github.com/strapi/strapi#getting-started) on how to setup a Strapi project.
|
|
||||||
|
|
||||||
#### 1. Fork the [repository](https://github.com/boazpoolman/strapi-plugin-config-sync)
|
#### 1. Fork the [repository](https://github.com/pluginpal/strapi-plugin-config-sync)
|
||||||
|
|
||||||
[Go to the repository](https://github.com/boazpoolman/strapi-plugin-config-sync) and fork it to your own GitHub account.
|
[Go to the repository](https://github.com/pluginpal/strapi-plugin-config-sync) and fork it to your own GitHub account.
|
||||||
|
|
||||||
#### 2. Clone from your repository into the plugins folder
|
#### 2. Clone the forked repository
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd YOUR_STRAPI_PROJECT/src/plugins
|
git clone git@github.com:YOUR_USERNAME/strapi-plugin-config-sync.git
|
||||||
git clone git@github.com:YOUR_USERNAME/strapi-plugin-config-sync.git config-sync
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 3. Install the dependencies
|
#### 3. Install the dependencies
|
||||||
|
|
||||||
Go to the plugin and install it's dependencies.
|
Go to the folder and install the dependencies
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd YOUR_STRAPI_PROJECT/src/plugins/config-sync/ && yarn plugin:install
|
cd strapi-plugin-config-sync && yarn install
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 4. Enable the plugin
|
#### 4. Install the playground dependencies
|
||||||
|
|
||||||
Add the following lines to the `config/plugins.js` file in your Strapi project.
|
Run this in the root of the repository
|
||||||
|
|
||||||
```
|
|
||||||
const path = require('path');
|
|
||||||
// ...
|
|
||||||
{
|
|
||||||
'config-sync': {
|
|
||||||
enabled: true,
|
|
||||||
resolve: path.resolve(__dirname, '../src/plugins/config-sync'),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 5. Rebuild your Strapi project
|
|
||||||
|
|
||||||
Rebuild your strapi project to build the admin part of the plugin.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd YOUR_STRAPI_PROJECT && yarn build
|
yarn playground:install
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 6. Running the administration panel in development mode
|
#### 5. Run the compiler of the plugin
|
||||||
|
|
||||||
**Start the administration panel server for development**
|
We use `yalc` to publish the package to a local registry. Run the following command o watch for changes and push to `yalc` every time a change is made:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd YOUR_STRAPI_PROJECT && yarn develop --watch-admin
|
yarn develop
|
||||||
```
|
```
|
||||||
|
|
||||||
The administration panel will be available at http://localhost:8080/admin
|
#### 6. Start the playground instance
|
||||||
|
|
||||||
|
Leave the watcher running, open up a new terminal window and browse back to the root of the plugin repo. Run the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn playground:develop
|
||||||
|
```
|
||||||
|
|
||||||
|
This will start the playground instance that will have the plugin installed from the `yalc` registry. Browse to http://localhost:1337 and create a test admin user to log in to the playground.
|
||||||
|
|
||||||
|
#### 7. Start your contribution!
|
||||||
|
|
||||||
|
You can now start working on your contribution. If you had trouble setting up this testing environment please feel free to report an issue on Github.
|
||||||
|
|
||||||
### Commit message convention
|
### Commit message convention
|
||||||
|
|
||||||
|
@ -82,12 +77,10 @@ The `package.json` file contains various scripts for common tasks:
|
||||||
|
|
||||||
- `yarn eslint`: lint files with ESLint.
|
- `yarn eslint`: lint files with ESLint.
|
||||||
- `yarn eslint:fix`: auto-fix ESLint issues.
|
- `yarn eslint:fix`: auto-fix ESLint issues.
|
||||||
- `yarn test:unit`: run unit tests with Jest.
|
- `yarn test:integration`: run integration tests with Jest.
|
||||||
|
|
||||||
### Sending a pull request
|
### Sending a pull request
|
||||||
|
|
||||||
> **Working on your first pull request?** You can learn how from this _free_ series: [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github).
|
|
||||||
|
|
||||||
When you're sending a pull request:
|
When you're sending a pull request:
|
||||||
|
|
||||||
- Prefer small pull requests focused on one change.
|
- Prefer small pull requests focused on one change.
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import React, { useState } from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import { Button } from '@strapi/design-system';
|
import { Button, Typography } from '@strapi/design-system';
|
||||||
import { Map } from 'immutable';
|
import { Map } from 'immutable';
|
||||||
import { getFetchClient, useNotification } from '@strapi/strapi/admin';
|
import { getFetchClient, useNotification } from '@strapi/strapi/admin';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
|
@ -14,38 +14,32 @@ const ActionButtons = () => {
|
||||||
const { post, get } = getFetchClient();
|
const { post, get } = getFetchClient();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const { toggleNotification } = useNotification();
|
const { toggleNotification } = useNotification();
|
||||||
const [modalIsOpen, setModalIsOpen] = useState(false);
|
|
||||||
const [actionType, setActionType] = useState('');
|
|
||||||
const partialDiff = useSelector((state) => state.getIn(['config', 'partialDiff'], Map({}))).toJS();
|
const partialDiff = useSelector((state) => state.getIn(['config', 'partialDiff'], Map({}))).toJS();
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
const closeModal = () => {
|
|
||||||
setActionType('');
|
|
||||||
setModalIsOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const openModal = (type) => {
|
|
||||||
setActionType(type);
|
|
||||||
setModalIsOpen(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ActionButtonsStyling>
|
<ActionButtonsStyling>
|
||||||
<Button disabled={isEmpty(partialDiff)} onClick={() => openModal('import')}>
|
|
||||||
{formatMessage({ id: 'config-sync.Buttons.Import' })}
|
|
||||||
</Button>
|
|
||||||
<Button disabled={isEmpty(partialDiff)} onClick={() => openModal('export')}>
|
|
||||||
{formatMessage({ id: 'config-sync.Buttons.Export' })}
|
|
||||||
</Button>
|
|
||||||
{!isEmpty(partialDiff) && (
|
|
||||||
<h4 style={{ display: 'inline' }}>{Object.keys(partialDiff).length} {Object.keys(partialDiff).length === 1 ? "config change" : "config changes"}</h4>
|
|
||||||
)}
|
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
isOpen={modalIsOpen}
|
type="import"
|
||||||
onClose={closeModal}
|
trigger={(
|
||||||
type={actionType}
|
<Button disabled={isEmpty(partialDiff)}>
|
||||||
onSubmit={(force) => actionType === 'import' ? dispatch(importAllConfig(partialDiff, force, toggleNotification, formatMessage, post, get)) : dispatch(exportAllConfig(partialDiff, toggleNotification, formatMessage, post, get))}
|
{formatMessage({ id: 'config-sync.Buttons.Import' })}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
onSubmit={(force) => dispatch(importAllConfig(partialDiff, force, toggleNotification, formatMessage, post, get))}
|
||||||
/>
|
/>
|
||||||
|
<ConfirmModal
|
||||||
|
type="export"
|
||||||
|
trigger={(
|
||||||
|
<Button disabled={isEmpty(partialDiff)}>
|
||||||
|
{formatMessage({ id: 'config-sync.Buttons.Export' })}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
onSubmit={(force) => dispatch(exportAllConfig(partialDiff, toggleNotification, formatMessage, post, get))}
|
||||||
|
/>
|
||||||
|
{!isEmpty(partialDiff) && (
|
||||||
|
<Typography variant="epsilon">{Object.keys(partialDiff).length} {Object.keys(partialDiff).length === 1 ? "config change" : "config changes"}</Typography>
|
||||||
|
)}
|
||||||
</ActionButtonsStyling>
|
</ActionButtonsStyling>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,36 +8,39 @@ import {
|
||||||
Typography,
|
Typography,
|
||||||
} from '@strapi/design-system';
|
} from '@strapi/design-system';
|
||||||
|
|
||||||
const ConfigDiff = ({ isOpen, onClose, oldValue, newValue, configName }) => {
|
const ConfigDiff = ({ oldValue, newValue, configName, trigger }) => {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
if (!isOpen) return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal.Root
|
<Modal.Root>
|
||||||
onClose={onClose}
|
<Modal.Trigger>
|
||||||
labelledBy="title"
|
{trigger}
|
||||||
>
|
</Modal.Trigger>
|
||||||
<Modal.Header>
|
<Modal.Content>
|
||||||
<Typography variant="omega" fontWeight="bold" textColor="neutral800">
|
<Modal.Header>
|
||||||
{formatMessage({ id: 'config-sync.ConfigDiff.Title' })} {configName}
|
<Typography variant="omega" fontWeight="bold" textColor="neutral800">
|
||||||
</Typography>
|
{formatMessage({ id: 'config-sync.ConfigDiff.Title' })} {configName}
|
||||||
</Modal.Header>
|
</Typography>
|
||||||
<Modal.Body>
|
</Modal.Header>
|
||||||
<Grid.Root paddingBottom={4} style={{ textAlign: 'center' }}>
|
<Modal.Body>
|
||||||
<Grid.Item col={6}>
|
<Grid.Root paddingBottom={4} style={{ textAlign: 'center' }}>
|
||||||
<Typography variant="delta">{formatMessage({ id: 'config-sync.ConfigDiff.SyncDirectory' })}</Typography>
|
<Grid.Item col={6}>
|
||||||
</Grid.Item>
|
<Typography variant="delta" style={{ width: '100%' }}>{formatMessage({ id: 'config-sync.ConfigDiff.SyncDirectory' })}</Typography>
|
||||||
<Grid.Item col={6}>
|
</Grid.Item>
|
||||||
<Typography variant="delta">{formatMessage({ id: 'config-sync.ConfigDiff.Database' })}</Typography>
|
<Grid.Item col={6}>
|
||||||
</Grid.Item>
|
<Typography variant="delta" style={{ width: '100%' }}>{formatMessage({ id: 'config-sync.ConfigDiff.Database' })}</Typography>
|
||||||
</Grid.Root>
|
</Grid.Item>
|
||||||
<ReactDiffViewer
|
</Grid.Root>
|
||||||
oldValue={JSON.stringify(oldValue, null, 2)}
|
<Typography variant="pi">
|
||||||
newValue={JSON.stringify(newValue, null, 2)}
|
<ReactDiffViewer
|
||||||
splitView
|
oldValue={JSON.stringify(oldValue, null, 2)}
|
||||||
compareMethod={DiffMethod.WORDS}
|
newValue={JSON.stringify(newValue, null, 2)}
|
||||||
/>
|
splitView
|
||||||
</Modal.Body>
|
compareMethod={DiffMethod.WORDS}
|
||||||
|
/>
|
||||||
|
</Typography>
|
||||||
|
</Modal.Body>
|
||||||
|
</Modal.Content>
|
||||||
</Modal.Root>
|
</Modal.Root>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Tr, Td, Checkbox } from '@strapi/design-system';
|
import { Tr, Td, Checkbox, Typography } from '@strapi/design-system';
|
||||||
|
|
||||||
const CustomRow = ({ row, checked, updateValue }) => {
|
const CustomRow = ({ row, checked, updateValue, ...props }) => {
|
||||||
const { configName, configType, state, onClick } = row;
|
const { configName, configType, state, onClick } = row;
|
||||||
|
|
||||||
const stateStyle = (stateStr) => {
|
const stateStyle = (stateStr) => {
|
||||||
|
@ -34,6 +34,7 @@ const CustomRow = ({ row, checked, updateValue }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tr
|
<Tr
|
||||||
|
{...props}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
if (e.target.type !== 'checkbox') {
|
if (e.target.type !== 'checkbox') {
|
||||||
onClick(configType, configName);
|
onClick(configType, configName);
|
||||||
|
@ -44,18 +45,18 @@ const CustomRow = ({ row, checked, updateValue }) => {
|
||||||
<Td>
|
<Td>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
aria-label={`Select ${configName}`}
|
aria-label={`Select ${configName}`}
|
||||||
value={checked}
|
checked={checked}
|
||||||
onValueChange={updateValue}
|
onCheckedChange={updateValue}
|
||||||
/>
|
/>
|
||||||
</Td>
|
</Td>
|
||||||
<Td>
|
<Td onClick={(e) => props.onClick(e)}>
|
||||||
<p>{configName}</p>
|
<Typography variant="omega">{configName}</Typography>
|
||||||
</Td>
|
</Td>
|
||||||
<Td>
|
<Td onClick={(e) => props.onClick(e)}>
|
||||||
<p>{configType}</p>
|
<Typography variant="omega">{configType}</Typography>
|
||||||
</Td>
|
</Td>
|
||||||
<Td>
|
<Td onClick={(e) => props.onClick(e)}>
|
||||||
<p style={stateStyle(state)}>{state}</p>
|
<Typography variant="omega" style={stateStyle(state)}>{state}</Typography>
|
||||||
</Td>
|
</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
);
|
);
|
||||||
|
|
|
@ -22,7 +22,6 @@ import { setConfigPartialDiffInState } from '../../state/actions/Config';
|
||||||
|
|
||||||
|
|
||||||
const ConfigList = ({ diff, isLoading }) => {
|
const ConfigList = ({ diff, isLoading }) => {
|
||||||
const [openModal, setOpenModal] = useState(false);
|
|
||||||
const [originalConfig, setOriginalConfig] = useState({});
|
const [originalConfig, setOriginalConfig] = useState({});
|
||||||
const [newConfig, setNewConfig] = useState({});
|
const [newConfig, setNewConfig] = useState({});
|
||||||
const [cName, setCname] = useState('');
|
const [cName, setCname] = useState('');
|
||||||
|
@ -72,7 +71,6 @@ const ConfigList = ({ diff, isLoading }) => {
|
||||||
setOriginalConfig(diff.fileConfig[`${configType}.${configName}`]);
|
setOriginalConfig(diff.fileConfig[`${configType}.${configName}`]);
|
||||||
setNewConfig(diff.databaseConfig[`${configType}.${configName}`]);
|
setNewConfig(diff.databaseConfig[`${configType}.${configName}`]);
|
||||||
setCname(`${configType}.${configName}`);
|
setCname(`${configType}.${configName}`);
|
||||||
setOpenModal(true);
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -89,13 +87,6 @@ const ConfigList = ({ diff, isLoading }) => {
|
||||||
dispatch(setConfigPartialDiffInState(newPartialDiff));
|
dispatch(setConfigPartialDiffInState(newPartialDiff));
|
||||||
}, [checkedItems]);
|
}, [checkedItems]);
|
||||||
|
|
||||||
const closeModal = () => {
|
|
||||||
setOriginalConfig({});
|
|
||||||
setNewConfig({});
|
|
||||||
setCname('');
|
|
||||||
setOpenModal(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div style={{ textAlign: 'center', marginTop: 40 }}>
|
<div style={{ textAlign: 'center', marginTop: 40 }}>
|
||||||
|
@ -117,22 +108,14 @@ const ConfigList = ({ diff, isLoading }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<ConfigDiff
|
|
||||||
isOpen={openModal}
|
|
||||||
oldValue={originalConfig}
|
|
||||||
newValue={newConfig}
|
|
||||||
onClose={closeModal}
|
|
||||||
configName={cName}
|
|
||||||
/>
|
|
||||||
<Table colCount={4} rowCount={rows.length + 1}>
|
<Table colCount={4} rowCount={rows.length + 1}>
|
||||||
<Thead>
|
<Thead>
|
||||||
<Tr>
|
<Tr>
|
||||||
<Th>
|
<Th>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
aria-label={formatMessage({ id: 'config-sync.ConfigList.SelectAll' })}
|
aria-label={formatMessage({ id: 'config-sync.ConfigList.SelectAll' })}
|
||||||
indeterminate={isIndeterminate}
|
checked={isIndeterminate ? "indeterminate" : allChecked}
|
||||||
onValueChange={(value) => setCheckedItems(checkedItems.map(() => value))}
|
onCheckedChange={(value) => setCheckedItems(checkedItems.map(() => value))}
|
||||||
value={allChecked}
|
|
||||||
/>
|
/>
|
||||||
</Th>
|
</Th>
|
||||||
<Th>
|
<Th>
|
||||||
|
@ -148,14 +131,21 @@ const ConfigList = ({ diff, isLoading }) => {
|
||||||
</Thead>
|
</Thead>
|
||||||
<Tbody>
|
<Tbody>
|
||||||
{rows.map((row, index) => (
|
{rows.map((row, index) => (
|
||||||
<ConfigListRow
|
<ConfigDiff
|
||||||
key={row.configName}
|
key={row.configName}
|
||||||
row={row}
|
oldValue={originalConfig}
|
||||||
checked={checkedItems[index]}
|
newValue={newConfig}
|
||||||
updateValue={() => {
|
configName={cName}
|
||||||
checkedItems[index] = !checkedItems[index];
|
trigger={(
|
||||||
setCheckedItems([...checkedItems]);
|
<ConfigListRow
|
||||||
}}
|
row={row}
|
||||||
|
checked={checkedItems[index]}
|
||||||
|
updateValue={() => {
|
||||||
|
checkedItems[index] = !checkedItems[index];
|
||||||
|
setCheckedItems([...checkedItems]);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Tbody>
|
</Tbody>
|
||||||
|
|
|
@ -10,69 +10,69 @@ import {
|
||||||
Checkbox,
|
Checkbox,
|
||||||
Divider,
|
Divider,
|
||||||
Box,
|
Box,
|
||||||
|
Field,
|
||||||
} from '@strapi/design-system';
|
} from '@strapi/design-system';
|
||||||
import { WarningCircle } from '@strapi/icons';
|
import { WarningCircle } from '@strapi/icons';
|
||||||
|
|
||||||
const ConfirmModal = ({ isOpen, onClose, onSubmit, type }) => {
|
const ConfirmModal = ({ onClose, onSubmit, type, trigger }) => {
|
||||||
const soft = useSelector((state) => state.getIn(['config', 'appEnv', 'config', 'soft'], false));
|
const soft = useSelector((state) => state.getIn(['config', 'appEnv', 'config', 'soft'], false));
|
||||||
const [force, setForce] = useState(false);
|
const [force, setForce] = useState(false);
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
if (!isOpen) return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.Root
|
<Dialog.Root>
|
||||||
onClose={onClose}
|
<Dialog.Trigger>
|
||||||
title={formatMessage({ id: "config-sync.popUpWarning.Confirmation" })}
|
{trigger}
|
||||||
isOpen={isOpen}
|
</Dialog.Trigger>
|
||||||
>
|
<Dialog.Content>
|
||||||
<Dialog.Body icon={<WarningCircle />}>
|
<Dialog.Header>{formatMessage({ id: "config-sync.popUpWarning.Confirmation" })}</Dialog.Header>
|
||||||
<Flex size={2}>
|
<Dialog.Body>
|
||||||
<Flex justifyContent="center">
|
<WarningCircle fill="danger600" width="32px" height="32px" />
|
||||||
<Typography variant="omega" id="confirm-description" style={{ textAlign: 'center' }}>
|
<Flex size={2}>
|
||||||
{formatMessage({ id: `config-sync.popUpWarning.warning.${type}_1` })}<br />
|
<Flex justifyContent="center">
|
||||||
{formatMessage({ id: `config-sync.popUpWarning.warning.${type}_2` })}
|
<Typography variant="omega" id="confirm-description" style={{ textAlign: 'center' }}>
|
||||||
</Typography>
|
{formatMessage({ id: `config-sync.popUpWarning.warning.${type}_1` })}<br />
|
||||||
|
{formatMessage({ id: `config-sync.popUpWarning.warning.${type}_2` })}
|
||||||
|
</Typography>
|
||||||
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
{(soft && type === 'import') && (
|
||||||
</Dialog.Body>
|
<Box width="100%">
|
||||||
{(soft && type === 'import') && (
|
<Divider marginTop={4} />
|
||||||
<React.Fragment>
|
<Box paddingTop={6}>
|
||||||
<Divider />
|
<Field.Root hint="Check this to ignore the soft setting.">
|
||||||
<Box padding={4}>
|
<Checkbox
|
||||||
<Checkbox
|
onValueChange={(value) => setForce(value)}
|
||||||
onValueChange={(value) => setForce(value)}
|
value={force}
|
||||||
value={force}
|
name="force"
|
||||||
name="force"
|
>
|
||||||
hint="Check this to ignore the soft setting."
|
{formatMessage({ id: 'config-sync.popUpWarning.force' })}
|
||||||
|
</Checkbox>
|
||||||
|
<Field.Hint />
|
||||||
|
</Field.Root>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Dialog.Body>
|
||||||
|
<Dialog.Footer>
|
||||||
|
<Dialog.Cancel>
|
||||||
|
<Button fullWidth variant="tertiary">
|
||||||
|
{formatMessage({ id: 'config-sync.popUpWarning.button.cancel' })}
|
||||||
|
</Button>
|
||||||
|
</Dialog.Cancel>
|
||||||
|
<Dialog.Action>
|
||||||
|
<Button
|
||||||
|
fullWidth
|
||||||
|
variant="secondary"
|
||||||
|
onClick={() => {
|
||||||
|
onSubmit(force);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{formatMessage({ id: 'config-sync.popUpWarning.force' })}
|
{formatMessage({ id: `config-sync.popUpWarning.button.${type}` })}
|
||||||
</Checkbox>
|
</Button>
|
||||||
</Box>
|
</Dialog.Action>
|
||||||
</React.Fragment>
|
</Dialog.Footer>
|
||||||
)}
|
</Dialog.Content>
|
||||||
<Dialog.Footer
|
|
||||||
startAction={(
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
onClose();
|
|
||||||
}}
|
|
||||||
variant="tertiary"
|
|
||||||
>
|
|
||||||
{formatMessage({ id: 'config-sync.popUpWarning.button.cancel' })}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
endAction={(
|
|
||||||
<Button
|
|
||||||
variant="secondary"
|
|
||||||
onClick={() => {
|
|
||||||
onClose();
|
|
||||||
onSubmit(force);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{formatMessage({ id: `config-sync.popUpWarning.button.${type}` })}
|
|
||||||
</Button>
|
|
||||||
)} />
|
|
||||||
</Dialog.Root>
|
</Dialog.Root>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState } from 'react';
|
import React from 'react';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import { getFetchClient, useNotification } from '@strapi/strapi/admin';
|
import { getFetchClient, useNotification } from '@strapi/strapi/admin';
|
||||||
|
@ -13,20 +13,21 @@ const FirstExport = () => {
|
||||||
const { post, get } = getFetchClient();
|
const { post, get } = getFetchClient();
|
||||||
const { toggleNotification } = useNotification();
|
const { toggleNotification } = useNotification();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const [modalIsOpen, setModalIsOpen] = useState(false);
|
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<ConfirmModal
|
|
||||||
isOpen={modalIsOpen}
|
|
||||||
onClose={() => setModalIsOpen(false)}
|
|
||||||
type="export"
|
|
||||||
onSubmit={() => dispatch(exportAllConfig([], toggleNotification, formatMessage, post, get))}
|
|
||||||
/>
|
|
||||||
<EmptyStateLayout
|
<EmptyStateLayout
|
||||||
content={formatMessage({ id: 'config-sync.FirstExport.Message' })}
|
content={formatMessage({ id: 'config-sync.FirstExport.Message' })}
|
||||||
action={<Button onClick={() => setModalIsOpen(true)}>{formatMessage({ id: 'config-sync.FirstExport.Button' })}</Button>}
|
action={(
|
||||||
|
<ConfirmModal
|
||||||
|
type="export"
|
||||||
|
onSubmit={() => dispatch(exportAllConfig([], toggleNotification, formatMessage, post, get))}
|
||||||
|
trigger={(
|
||||||
|
<Button>{formatMessage({ id: 'config-sync.FirstExport.Button' })}</Button>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
icon={<EmptyDocuments width={160} />}
|
icon={<EmptyDocuments width={160} />}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
21
package.json
21
package.json
|
@ -14,12 +14,15 @@
|
||||||
"config-sync": "./bin/config-sync"
|
"config-sync": "./bin/config-sync"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"develop": "nodemon -e js,jsx --ignore playground --exec \"yalc publish && yalc push\"",
|
||||||
|
"build": "yalc push --publish",
|
||||||
"eslint": "eslint --max-warnings=0 './**/*.{js,jsx}'",
|
"eslint": "eslint --max-warnings=0 './**/*.{js,jsx}'",
|
||||||
"eslint:fix": "eslint --fix './**/*.{js,jsx}'",
|
"eslint:fix": "eslint --fix './**/*.{js,jsx}'",
|
||||||
"test:unit": "jest --verbose",
|
"test:unit": "jest --verbose",
|
||||||
"test:integration": "cd playground && node_modules/.bin/jest --verbose --forceExit --detectOpenHandles",
|
"test:integration": "cd playground && node_modules/.bin/jest --verbose --forceExit --detectOpenHandles",
|
||||||
"plugin:install": "yarn install && rm -rf node_modules/@strapi/helper-plugin",
|
"playground:install": "yarn playground:yalc-add-link && cd playground && yarn install",
|
||||||
"playground:install": "cd playground && yarn install",
|
"playground:yalc-add": "cd playground && yalc add strapi-plugin-config-sync",
|
||||||
|
"playground:yalc-add-link": "cd playground && yalc add --link strapi-plugin-config-sync",
|
||||||
"playground:build": "cd playground && yarn build",
|
"playground:build": "cd playground && yarn build",
|
||||||
"playground:develop": "cd playground && yarn develop"
|
"playground:develop": "cd playground && yarn develop"
|
||||||
},
|
},
|
||||||
|
@ -55,13 +58,13 @@
|
||||||
"strapi-server.js"
|
"strapi-server.js"
|
||||||
],
|
],
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@strapi/strapi": "^5.0.0-rc.2"
|
"@strapi/strapi": "^5.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@strapi/design-system": "2.0.0-rc.7",
|
"@strapi/design-system": "2.0.0-rc.11",
|
||||||
"@strapi/icons": "2.0.0-rc.7",
|
"@strapi/icons": "2.0.0-rc.11",
|
||||||
"@strapi/strapi": "5.0.0-rc.2",
|
"@strapi/strapi": "5.0.4",
|
||||||
"@strapi/utils": "5.0.0-rc.2",
|
"@strapi/utils": "5.0.4",
|
||||||
"babel-eslint": "9.0.0",
|
"babel-eslint": "9.0.0",
|
||||||
"eslint": "^7.32.0",
|
"eslint": "^7.32.0",
|
||||||
"eslint-config-airbnb": "^18.2.1",
|
"eslint-config-airbnb": "^18.2.1",
|
||||||
|
@ -78,10 +81,12 @@
|
||||||
"jest-cli": "^29.3.1",
|
"jest-cli": "^29.3.1",
|
||||||
"jest-styled-components": "^7.0.2",
|
"jest-styled-components": "^7.0.2",
|
||||||
"lodash": "^4.17.11",
|
"lodash": "^4.17.11",
|
||||||
|
"nodemon": "^3.1.7",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-redux": "^7.2.2",
|
"react-redux": "^7.2.2",
|
||||||
"redux": "^4.0.5",
|
"redux": "^4.0.5",
|
||||||
"styled-components": "^5.2.3"
|
"styled-components": "^5.2.3",
|
||||||
|
"yalc": "^1.0.0-pre.53"
|
||||||
},
|
},
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/pluginpal/strapi-plugin-config-sync/issues"
|
"url": "https://github.com/pluginpal/strapi-plugin-config-sync/issues"
|
||||||
|
|
|
@ -114,3 +114,7 @@ exports
|
||||||
*.cache
|
*.cache
|
||||||
build
|
build
|
||||||
.strapi-updater.json
|
.strapi-updater.json
|
||||||
|
|
||||||
|
# yalc
|
||||||
|
.yalc
|
||||||
|
yalc.lock
|
||||||
|
|
|
@ -2,15 +2,15 @@
|
||||||
* This file was automatically generated by Strapi.
|
* This file was automatically generated by Strapi.
|
||||||
* Any modifications made will be discarded.
|
* Any modifications made will be discarded.
|
||||||
*/
|
*/
|
||||||
import usersPermissions from "@strapi/plugin-users-permissions/strapi-admin";
|
|
||||||
import strapiCloud from "@strapi/plugin-cloud/strapi-admin";
|
import strapiCloud from "@strapi/plugin-cloud/strapi-admin";
|
||||||
|
import usersPermissions from "@strapi/plugin-users-permissions/strapi-admin";
|
||||||
import configSync from "strapi-plugin-config-sync/strapi-admin";
|
import configSync from "strapi-plugin-config-sync/strapi-admin";
|
||||||
import { renderAdmin } from "@strapi/strapi/admin";
|
import { renderAdmin } from "@strapi/strapi/admin";
|
||||||
|
|
||||||
renderAdmin(document.getElementById("strapi"), {
|
renderAdmin(document.getElementById("strapi"), {
|
||||||
plugins: {
|
plugins: {
|
||||||
"users-permissions": usersPermissions,
|
|
||||||
"strapi-cloud": strapiCloud,
|
"strapi-cloud": strapiCloud,
|
||||||
|
"users-permissions": usersPermissions,
|
||||||
"config-sync": configSync,
|
"config-sync": configSync,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,4 +14,7 @@ module.exports = ({ env }) => ({
|
||||||
nps: env.bool('FLAG_NPS', true),
|
nps: env.bool('FLAG_NPS', true),
|
||||||
promoteEE: env.bool('FLAG_PROMOTE_EE', true),
|
promoteEE: env.bool('FLAG_PROMOTE_EE', true),
|
||||||
},
|
},
|
||||||
|
watchIgnoreFiles: [
|
||||||
|
'!**/.yalc/**/server/**',
|
||||||
|
]
|
||||||
});
|
});
|
||||||
|
|
|
@ -13,17 +13,18 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"jest-cli": "^29.7.0",
|
"jest-cli": "^29.7.0",
|
||||||
"supertest": "^6.3.3"
|
"supertest": "^6.3.3",
|
||||||
|
"yalc": "^1.0.0-pre.53"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@strapi/strapi": "5.0.0-rc.2",
|
"@strapi/plugin-cloud": "5.0.4",
|
||||||
"@strapi/plugin-users-permissions": "5.0.0-rc.2",
|
"@strapi/plugin-users-permissions": "5.0.4",
|
||||||
"@strapi/plugin-cloud": "5.0.0-rc.2",
|
"@strapi/strapi": "5.0.4",
|
||||||
"better-sqlite3": "9.4.3",
|
"better-sqlite3": "9.4.3",
|
||||||
"react": "^18.0.0",
|
"react": "^18.0.0",
|
||||||
"react-dom": "^18.0.0",
|
"react-dom": "^18.0.0",
|
||||||
"react-router-dom": "^6.0.0",
|
"react-router-dom": "^6.0.0",
|
||||||
"strapi-plugin-config-sync": "./..",
|
"strapi-plugin-config-sync": "link:.yalc/strapi-plugin-config-sync",
|
||||||
"styled-components": "^6.0.0"
|
"styled-components": "^6.0.0"
|
||||||
},
|
},
|
||||||
"author": {
|
"author": {
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import type { Struct, Schema } from '@strapi/strapi';
|
/*
|
||||||
|
* The app doesn't have any components yet.
|
||||||
declare module '@strapi/strapi' {
|
*/
|
||||||
export module Public {}
|
|
||||||
}
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ export interface ApiHomeHome extends Struct.SingleTypeSchema {
|
||||||
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||||
Schema.Attribute.Private;
|
Schema.Attribute.Private;
|
||||||
locale: Schema.Attribute.String;
|
locale: Schema.Attribute.String;
|
||||||
|
localizations: Schema.Attribute.Relation<'oneToMany', 'api::home.home'>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,6 +46,7 @@ export interface ApiPagePage extends Struct.CollectionTypeSchema {
|
||||||
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||||
Schema.Attribute.Private;
|
Schema.Attribute.Private;
|
||||||
locale: Schema.Attribute.String;
|
locale: Schema.Attribute.String;
|
||||||
|
localizations: Schema.Attribute.Relation<'oneToMany', 'api::page.page'>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,6 +101,10 @@ export interface PluginUploadFile extends Struct.CollectionTypeSchema {
|
||||||
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||||
Schema.Attribute.Private;
|
Schema.Attribute.Private;
|
||||||
locale: Schema.Attribute.String;
|
locale: Schema.Attribute.String;
|
||||||
|
localizations: Schema.Attribute.Relation<
|
||||||
|
'oneToMany',
|
||||||
|
'plugin::upload.file'
|
||||||
|
>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,6 +151,10 @@ export interface PluginUploadFolder extends Struct.CollectionTypeSchema {
|
||||||
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||||
Schema.Attribute.Private;
|
Schema.Attribute.Private;
|
||||||
locale: Schema.Attribute.String;
|
locale: Schema.Attribute.String;
|
||||||
|
localizations: Schema.Attribute.Relation<
|
||||||
|
'oneToMany',
|
||||||
|
'plugin::upload.folder'
|
||||||
|
>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,6 +196,10 @@ export interface PluginI18NLocale extends Struct.CollectionTypeSchema {
|
||||||
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||||
Schema.Attribute.Private;
|
Schema.Attribute.Private;
|
||||||
locale: Schema.Attribute.String;
|
locale: Schema.Attribute.String;
|
||||||
|
localizations: Schema.Attribute.Relation<
|
||||||
|
'oneToMany',
|
||||||
|
'plugin::i18n.locale'
|
||||||
|
>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,6 +243,10 @@ export interface PluginContentReleasesRelease
|
||||||
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||||
Schema.Attribute.Private;
|
Schema.Attribute.Private;
|
||||||
locale: Schema.Attribute.String;
|
locale: Schema.Attribute.String;
|
||||||
|
localizations: Schema.Attribute.Relation<
|
||||||
|
'oneToMany',
|
||||||
|
'plugin::content-releases.release'
|
||||||
|
>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,6 +287,10 @@ export interface PluginContentReleasesReleaseAction
|
||||||
Schema.Attribute.Private;
|
Schema.Attribute.Private;
|
||||||
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||||
Schema.Attribute.Private;
|
Schema.Attribute.Private;
|
||||||
|
localizations: Schema.Attribute.Relation<
|
||||||
|
'oneToMany',
|
||||||
|
'plugin::content-releases.release-action'
|
||||||
|
>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,6 +334,10 @@ export interface PluginReviewWorkflowsWorkflow
|
||||||
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||||
Schema.Attribute.Private;
|
Schema.Attribute.Private;
|
||||||
locale: Schema.Attribute.String;
|
locale: Schema.Attribute.String;
|
||||||
|
localizations: Schema.Attribute.Relation<
|
||||||
|
'oneToMany',
|
||||||
|
'plugin::review-workflows.workflow'
|
||||||
|
>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,6 +379,10 @@ export interface PluginReviewWorkflowsWorkflowStage
|
||||||
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||||
Schema.Attribute.Private;
|
Schema.Attribute.Private;
|
||||||
locale: Schema.Attribute.String;
|
locale: Schema.Attribute.String;
|
||||||
|
localizations: Schema.Attribute.Relation<
|
||||||
|
'oneToMany',
|
||||||
|
'plugin::review-workflows.workflow-stage'
|
||||||
|
>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -391,6 +421,10 @@ export interface PluginUsersPermissionsPermission
|
||||||
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||||
Schema.Attribute.Private;
|
Schema.Attribute.Private;
|
||||||
locale: Schema.Attribute.String;
|
locale: Schema.Attribute.String;
|
||||||
|
localizations: Schema.Attribute.Relation<
|
||||||
|
'oneToMany',
|
||||||
|
'plugin::users-permissions.permission'
|
||||||
|
>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -439,6 +473,10 @@ export interface PluginUsersPermissionsRole
|
||||||
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||||
Schema.Attribute.Private;
|
Schema.Attribute.Private;
|
||||||
locale: Schema.Attribute.String;
|
locale: Schema.Attribute.String;
|
||||||
|
localizations: Schema.Attribute.Relation<
|
||||||
|
'oneToMany',
|
||||||
|
'plugin::users-permissions.role'
|
||||||
|
>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -490,6 +528,10 @@ export interface PluginUsersPermissionsUser
|
||||||
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||||
Schema.Attribute.Private;
|
Schema.Attribute.Private;
|
||||||
locale: Schema.Attribute.String;
|
locale: Schema.Attribute.String;
|
||||||
|
localizations: Schema.Attribute.Relation<
|
||||||
|
'oneToMany',
|
||||||
|
'plugin::users-permissions.user'
|
||||||
|
>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -535,6 +577,7 @@ export interface AdminPermission extends Struct.CollectionTypeSchema {
|
||||||
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||||
Schema.Attribute.Private;
|
Schema.Attribute.Private;
|
||||||
locale: Schema.Attribute.String;
|
locale: Schema.Attribute.String;
|
||||||
|
localizations: Schema.Attribute.Relation<'oneToMany', 'admin::permission'>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -599,6 +642,7 @@ export interface AdminUser extends Struct.CollectionTypeSchema {
|
||||||
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||||
Schema.Attribute.Private;
|
Schema.Attribute.Private;
|
||||||
locale: Schema.Attribute.String;
|
locale: Schema.Attribute.String;
|
||||||
|
localizations: Schema.Attribute.Relation<'oneToMany', 'admin::user'>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -646,6 +690,7 @@ export interface AdminRole extends Struct.CollectionTypeSchema {
|
||||||
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||||
Schema.Attribute.Private;
|
Schema.Attribute.Private;
|
||||||
locale: Schema.Attribute.String;
|
locale: Schema.Attribute.String;
|
||||||
|
localizations: Schema.Attribute.Relation<'oneToMany', 'admin::role'>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -704,6 +749,7 @@ export interface AdminApiToken extends Struct.CollectionTypeSchema {
|
||||||
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||||
Schema.Attribute.Private;
|
Schema.Attribute.Private;
|
||||||
locale: Schema.Attribute.String;
|
locale: Schema.Attribute.String;
|
||||||
|
localizations: Schema.Attribute.Relation<'oneToMany', 'admin::api-token'>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -742,6 +788,10 @@ export interface AdminApiTokenPermission extends Struct.CollectionTypeSchema {
|
||||||
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||||
Schema.Attribute.Private;
|
Schema.Attribute.Private;
|
||||||
locale: Schema.Attribute.String;
|
locale: Schema.Attribute.String;
|
||||||
|
localizations: Schema.Attribute.Relation<
|
||||||
|
'oneToMany',
|
||||||
|
'admin::api-token-permission'
|
||||||
|
>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -797,6 +847,10 @@ export interface AdminTransferToken extends Struct.CollectionTypeSchema {
|
||||||
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||||
Schema.Attribute.Private;
|
Schema.Attribute.Private;
|
||||||
locale: Schema.Attribute.String;
|
locale: Schema.Attribute.String;
|
||||||
|
localizations: Schema.Attribute.Relation<
|
||||||
|
'oneToMany',
|
||||||
|
'admin::transfer-token'
|
||||||
|
>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -836,6 +890,10 @@ export interface AdminTransferTokenPermission
|
||||||
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||||
Schema.Attribute.Private;
|
Schema.Attribute.Private;
|
||||||
locale: Schema.Attribute.String;
|
locale: Schema.Attribute.String;
|
||||||
|
localizations: Schema.Attribute.Relation<
|
||||||
|
'oneToMany',
|
||||||
|
'admin::transfer-token-permission'
|
||||||
|
>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue