/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import {
SchemaSettingsItem,
useAPIClient,
useDesignable,
useFormBlockProps,
usePlugin,
SchemaSettingsDivider,
} from '@nocobase/client';
import { useFieldSchema, useForm, useField } from '@formily/react';
import { App } from 'antd';
import React from 'react';
import _ from 'lodash';
import { blockKeepProps, convertTplBlock, formSchemaPatch } from '../initializers/TemplateBlockInitializer';
import { Schema } from '@formily/json-schema';
import { useT } from '../locale';
import PluginBlockTemplateClient from '..';
import { addToolbarClass, syncExtraTemplateInfo } from '../utils/template';
import { uid } from '@nocobase/utils/client';
const findInsertPosition = (parentSchema, uid) => {
const postion = {
insertPosition: 'beforeBegin',
insertTarget: null,
};
const properties = Object.values(parentSchema.properties || {}).sort((a, b) => {
return (a as any)['x-index'] - (b as any)['x-index'];
});
for (let i = 0; i < properties.length; i++) {
const property = properties[i];
if ((property as any)['x-uid'] === uid) {
postion.insertPosition = 'beforeBegin';
if (i === properties.length - 1) {
postion.insertPosition = 'beforeEnd';
postion.insertTarget = parentSchema['x-uid'];
} else {
postion.insertPosition = 'beforeBegin';
postion.insertTarget = (properties[i + 1] as any)['x-uid'];
}
}
}
return postion;
};
const findParentRootTemplateSchema = (fieldSchema) => {
if (!fieldSchema) {
return null;
}
if (fieldSchema['x-template-root-uid']) {
return fieldSchema;
} else {
return findParentRootTemplateSchema(fieldSchema.parent);
}
};
export const RevertSetting = () => {
const { refresh, remove } = useDesignable();
const plugin = usePlugin(PluginBlockTemplateClient);
const t = useT();
const api = useAPIClient();
const form = useForm();
const field = useField();
// const { runAsync } = useDataBlockRequest();
const { form: blockForm } = useFormBlockProps();
const fieldSchema = useFieldSchema();
const { modal, message } = App.useApp();
return (
<>
{
modal.confirm({
title: t('Revert to template'),
content: t('Are you sure you want to revert all changes from the template?'),
...confirm,
async onOk() {
const templateSchemaId = _.get(fieldSchema, 'x-template-uid');
const res = await api.request({
url: `/uiSchemas:getJsonSchema/${templateSchemaId}`,
});
const templateSchema = res.data?.data;
if (!templateSchema?.['x-uid']) {
// this means the template has already been deleted
remove(null, {
removeParentsIfNoChildren: true,
breakRemoveOn: {
'x-component': 'Grid',
},
});
refresh({ refreshParentSchema: true });
form.reset();
form.clearFormGraph();
blockForm?.clearFormGraph();
message.success(t('Reset successfully'), 0.2);
return;
}
const rootSchema = findParentRootTemplateSchema(fieldSchema);
const isRoot = rootSchema === fieldSchema;
if (isRoot) {
plugin.setTemplateCache(templateSchema);
} else {
// patch the edit form button schema, keep same as the form
if (fieldSchema['x-settings']?.includes('updateSubmit')) {
templateSchema['x-settings'] = 'actionSettings:updateSubmit';
templateSchema['x-use-component-props'] = 'useUpdateActionProps';
}
}
// patch filter block
// remove this when multiple blocks template supported
if (fieldSchema['x-filter-targets']) {
templateSchema['x-filter-targets'] = fieldSchema['x-filter-targets'];
}
const newSchema = convertTplBlock(
templateSchema,
false,
isRoot,
rootSchema?.['x-uid'],
rootSchema?.['x-block-template-key'],
);
newSchema['x-index'] = fieldSchema['x-index'];
for (const p of blockKeepProps) {
if (_.hasIn(fieldSchema, p)) {
_.set(newSchema, p, _.get(fieldSchema, p));
}
}
if (
fieldSchema['x-decorator'] === 'FormBlockProvider' &&
fieldSchema['x-use-decorator-props'] === 'useEditFormBlockDecoratorProps'
) {
formSchemaPatch(newSchema, {
collectionName: fieldSchema['x-decorator-props']['collection'],
dataSourceName: fieldSchema['x-decorator-props']['dataSource'],
association: fieldSchema['x-decorator-props']['association'],
currentRecord: true,
});
}
// remove old schema
const position = findInsertPosition(fieldSchema.parent, fieldSchema['x-uid']);
await api.request({
url: `/uiSchemas:remove/${fieldSchema['x-uid']}`,
});
// insertAdjacent
const schema = new Schema(newSchema);
schema.name = fieldSchema.name;
await api.request({
url: `/uiSchemas:insertAdjacent/${position.insertTarget}?position=${position.insertPosition}`,
method: 'post',
data: {
schema,
},
});
// this is a hack to make the schema component refresh to the new schema
fieldSchema.toJSON = () => {
let ret;
if (schema['x-template-root-uid'] || fieldSchema.parent?.['x-template-root-uid']) {
ret = schema.toJSON();
} else {
const mergedSchema = _.merge(templateSchema, schema.toJSON());
ret = mergedSchema;
}
addToolbarClass(ret);
syncExtraTemplateInfo(ret, plugin.templateInfos, plugin.savedSchemaUids);
return ret;
};
refresh({ refreshParentSchema: true });
// set componentProps, otherwise some components props will not be refreshed
field['componentProps'] = {
...templateSchema['x-component-props'],
key: uid(),
};
if (field.parent?.['componentProps']) {
field.parent['componentProps'] = {
...field.parent['componentProps'],
key: uid(),
};
}
// set decoratorProps, otherwise title will not be refreshed
field['decoratorProps'] = {
...field['decoratorProps'],
...templateSchema['x-decorator-props'],
key: uid(),
};
if (field.parent?.['decoratorProps']) {
field.parent['decoratorProps'] = {
...field.parent['decoratorProps'],
key: uid(),
};
}
form.reset();
blockForm?.reset();
form.clearFormGraph('*', false);
blockForm?.clearFormGraph('*', false);
message.success(t('Reset successfully'), 0.2);
},
});
}}
>
{t('Revert to template')}
>
);
};