# Auto generation UI
## Points
- Interface builder for blades (and potentially not only).
- We need to implement a way to render the configuration specified in the JSON file for Admin & Beeopen.
- The JSON/JS file will correspond to the configuration regulated with Mike & Andy.
- Platforms that have a more complex UI will be implemented separately as code in Admin/Beeopen.
- Old platforms are not planned for processing into a new format, until then there will be free time and desire. It is planned to apply this feature for new platforms.
## Blades that will use this pattern rendering
Rake Admin:
- Bot platforms blade
- Bot platform blade
- Platform app configuration blade
- platform list
Beeopen:
- list here
## Structure
### v1
The configuration will be a format similar to React.createElement
An example of an array of fields:
```typescript=
const scope = {
currentDate: new Date().toString(),
focused: {
appId: true
},
request: (url: string) => {
}
}
[{
type: 'Input',
// в props -> любые поля которые будут поддерживаться компонентом.
// нужно учитывать, что то что в коде можно было использовать напрямую
// ( внешние переменные ), теперь нужно будет "ловить" посредствам
// переданных параметров из конфигурации.
props: {
name: 'appId',
label: 'Results for: $var(currentDate) $var(focused.appId)',
onChange: (value, context) => {
},
onBlur: async (_, context) => {
const response = await context.request('/validate',"POST", {
appId: context.values.appId
});
context.setScope({
valid: {
appId: response.data
}
})
},
onFocus: (_, context) => {
}
},
}, {
type: 'Panel',
props: {
label: 'Some set on fields',
children: [
{
type: 'Input',
...
}
]
}
}]
```
### v2
There is a separate repository, it has an entry point js file (or ts). There is a code that describes each platform (in the format of an object) in the form of js (ts) code, and subsequently the object of each platform is imported into the file, in which an object is formed from these platforms and is written globally in the window.
Then this code is collected in a single JS file and deployed to the server / bucket.
Subsequently, the assembled and deployed script is connected as <script /> before the application code (Admin / Beeopen) and in this case the global object becomes available in the application code. And the application in places where you need to render a specific part (let's say a blade) of the platform accesses this global object.
The approach with a separate repository gives us better supportability, it will be possible to cover this code with tests, and in this case it will be more clear where exactly the error occurred (and it will not be necessary to reload the platform code 500 times, but you can understand where the error is in unit tests). Also, this code will be typed, since it will be possible to write it using Typescript
```typescript=
const twilio = (context : AppContext) => {
return {
platformBlade: {
type: 'Blade',
children: [{
type: 'Input',
label: `${context.currentDate}`,
onBlur: (value : string, context: PlatformBladeContext) => {
...
}
}]
}
}
```
## Prototype
### Rake direct
```javascript=
const scope = {
request: {
get: () => { },
},
icons: {
edit: <svg></svg>,
delete: <svg></svg>
}
};
module.exports = (scope) => {
const state = { // ReduxState
visible: false,
platformId: 6,
};
const element = {
setVisible: (value) => state.visible = value,
getVisible: () => state.visible,
};
return {
type: 'platform-panel',
props: {
// icon: 'http://',
// title: 'Rake direct',
serviceIds: [adminServiceId, rakeUserAppServiceId],
onInit: async (_, context) => {
const { botId, element } = context;
element.element = element;
const data = await scope.request.get(`http://../rake-direct/${botId}`);
state.haveBots = data.data.length != 0;
state.isGlobalEnabled = Boolean(data.data.find(e => e.isEnabled));
state.rows = data.data.length;
state.data = data.data; // List of bots
const platformInfo = await scope.request.get(`http://../platforms/${state.platformId}`);
state.platformName = platformInfo.data.name;
state.icon = platformInfo.data.icon;
element.title = state.platformName;
element.icon = state.icon;
element.statusIndicator = state.isGlobalEnabled;
},
onClick: (_, context) => {
element.setVisible(!element.getVisible());
},
// description: 'Rake Direct Config Name',
children: [
{
serviceIds: [adminServiceId],
type: 'table',
props: {
cols: 4,
title: ['$var(state.platformName) Config Name'],
groups: [75, 8.3, 8.3, 8.3],
content: () => {
const { data } = state;
return data.map(e => ([
{ type: 'string', value: e.name },
{
type: 'icon',
icon: scope.icons.edit,
onHover: () => {
scope.spawn.tooltip({ text: 'Edit' });
},
onClick: () => {
state.spawn.blade({
icon: state.icon,
title: 'Manage $var(state.platformName) Platform',
description: 'Edit $var(state.platformName) Configuration',
onInit: () => {
state.botBlade = {
configurationId: e.rakeDirectConfigurationId,
isEnabled: e.isEnabled,
isChanged: false,
};
},
children: [
{
type: 'selector',
onInit: () => { },
onChange: (id) => {
state.botBlade.isChanged = true;
state.botBlade.configurationId = id;
}
},
{
type: 'checkBox',
text: 'Enabled',
value: e.isEnabled,
onChange: (value) => {
state.botBlade.isEnabled = value;
}
},
{
type: 'row-flex',
children: [
{
type: 'button',
text: 'save',
onClick: (element: Blade) => {
if (state.botBlade.isChanged && e.isEnabled == false) {
return;
}
if (state.botBlade.isEnabled == e.isEnabled && !state.botBlade.isChanged) {
return;
}
const action = '...';
const body = {
action,
};
const data = await scope.request.put(`http://../rake-direct/${botId}`, body);
element.close();
}
},
{ type: 'button', text: 'cancel', onClick: (element: Blade) => element.close() },
]
}
]
});
}
},
{
type: 'action',
icon: scope.icons.delete,
onClick: () => {
scope.spawn.modalWindow({
title: 'Are you sure?',
description: 'This action cannot be undone',
buttons: [
{
title: 'Cancel',
onClick: (element: ModalWindow) => element.close()
},
{
title: 'Delete',
onClick: (element: ModalWindow) => {
try {
await scope.request.delete(`http://../rake-direct/${botId}/rakeDirectId/${e.rakeDirectId}`);
state.data = state.data.filter(elem => elem.rakeDirectId != e.rakeDirectId);
//if redux state not work
//scope.rerender(element.element);
} catch (err) {
scope.spawn.errorMessage({ text: 'Something go wrong' });
}
element.close();
}
}
]
})
}
},
{ type: 'status-indicator', value: () => e.isEnabled },
]))
}
}
}
]
}
}
};
```