346 lines
10 KiB
JavaScript
346 lines
10 KiB
JavaScript
import {hierarchy as hierarchyFunctions,
|
|
common, getTemplateApi } from "budibase-core";
|
|
import {filter, cloneDeep, sortBy, map, last,
|
|
find, isEmpty, groupBy, reduce} from "lodash/fp";
|
|
import {chain, getNode, validate,
|
|
constructHierarchy, templateApi} from "../common/core";
|
|
import {writable} from "svelte/store";
|
|
|
|
export const getStore = () => {
|
|
|
|
const initial = {
|
|
apps:[],
|
|
appname:"",
|
|
hierarchy: {},
|
|
actions: [],
|
|
triggers: [],
|
|
currentNodeIsNew: false,
|
|
errors: [],
|
|
activeNav: "database",
|
|
hasAppPackage: false,
|
|
accessLevels: [],
|
|
currentNode: null};
|
|
|
|
const store = writable(initial);
|
|
|
|
store.initialise = initialise(store, initial);
|
|
store.newChildRecord = newRecord(store, false);
|
|
store.newRootRecord = newRecord(store, true);
|
|
store.selectExistingNode = selectExistingNode(store);
|
|
store.newChildIndex = newIndex(store, false);
|
|
store.newRootIndex = newIndex(store, true);
|
|
store.saveCurrentNode = saveCurrentNode(store);
|
|
store.importAppDefinition = importAppDefinition(store);
|
|
store.deleteCurrentNode = deleteCurrentNode(store);
|
|
store.saveField = saveField(store);
|
|
store.deleteField = deleteField(store);
|
|
store.saveAction = saveAction(store);
|
|
store.deleteAction = deleteAction(store);
|
|
store.saveTrigger = saveTrigger(store);
|
|
store.deleteTrigger = deleteTrigger(store);
|
|
store.saveLevel = saveLevel(store);
|
|
store.deleteLevel = deleteLevel(store);
|
|
store.setActiveNav = setActiveNav(store);
|
|
return store;
|
|
}
|
|
|
|
export default getStore;
|
|
|
|
const initialise = (store, initial) => async () => {
|
|
|
|
const appname = window.location.hash
|
|
? last(window.location.hash.substr(1).split("/"))
|
|
: "";
|
|
|
|
if(!appname) {
|
|
initial.apps = await fetch(`/_builder/api/apps`)
|
|
.then(r => r.json());
|
|
initial.hasAppPackage = false;
|
|
store.set(initial);
|
|
return initial;
|
|
}
|
|
|
|
const pkg = await fetch(`/_builder/api/${appname}/appPackage`)
|
|
.then(r => r.json());
|
|
|
|
initial.appname = appname;
|
|
initial.hasAppPackage = true;
|
|
initial.hierarchy = pkg.appDefinition.hierarchy;
|
|
initial.accessLevels = pkg.accessLevels;
|
|
initial.actions = reduce((arr, action) => {
|
|
arr.push(action);
|
|
return arr;
|
|
})(pkg.appDefinition.actions, []);
|
|
initial.triggers = pkg.appDefinition.triggers;
|
|
|
|
if(!!initial.hierarchy && !isEmpty(initial.hierarchy)) {
|
|
initial.hierarchy = constructHierarchy(initial.hierarchy);
|
|
const shadowHierarchy = createShadowHierarchy(initial.hierarchy);
|
|
if(initial.currentNode !== null)
|
|
initial.currentNode = getNode(
|
|
shadowHierarchy, initial.currentNode.nodeId
|
|
);
|
|
}
|
|
store.set(initial);
|
|
return initial;
|
|
}
|
|
|
|
const newRecord = (store, useRoot) => () => {
|
|
store.update(s => {
|
|
s.currentNodeIsNew = true;
|
|
const shadowHierarchy = createShadowHierarchy(s.hierarchy);
|
|
parent = useRoot ? shadowHierarchy
|
|
: getNode(
|
|
shadowHierarchy,
|
|
s.currentNode.nodeId);
|
|
s.errors = [];
|
|
s.currentNode = templateApi(shadowHierarchy)
|
|
.getNewRecordTemplate(parent, "", true);
|
|
return s;
|
|
});
|
|
}
|
|
|
|
|
|
const selectExistingNode = (store) => (nodeId) => {
|
|
store.update(s => {
|
|
const shadowHierarchy = createShadowHierarchy(s.hierarchy);
|
|
s.currentNode = getNode(
|
|
shadowHierarchy, nodeId
|
|
);
|
|
s.currentNodeIsNew = false;
|
|
s.errors = [];
|
|
return s;
|
|
})
|
|
}
|
|
|
|
const newIndex = (store, useRoot) => () => {
|
|
store.update(s => {
|
|
s.currentNodeIsNew = true;
|
|
s.errors = [];
|
|
const shadowHierarchy = createShadowHierarchy(s.hierarchy);
|
|
parent = useRoot ? shadowHierarchy
|
|
: getNode(
|
|
shadowHierarchy,
|
|
s.currentNode.nodeId);
|
|
|
|
s.currentNode = templateApi(shadowHierarchy)
|
|
.getNewIndexTemplate(parent);
|
|
return s;
|
|
});
|
|
}
|
|
|
|
const saveCurrentNode = (store) => () => {
|
|
store.update(s => {
|
|
|
|
const errors = validate.node(s.currentNode);
|
|
s.errors = errors;
|
|
if(errors.length > 0) {
|
|
return s;
|
|
}
|
|
|
|
const parentNode = getNode(
|
|
s.hierarchy, s.currentNode.parent().nodeId);
|
|
|
|
const existingNode = getNode(
|
|
s.hierarchy, s.currentNode.nodeId);
|
|
|
|
let index = parentNode.children.length;
|
|
if(!!existingNode) {
|
|
// remove existing
|
|
index = existingNode.parent().children.indexOf(existingNode);
|
|
existingNode.parent().children = chain(existingNode.parent().children, [
|
|
filter(c => c.nodeId !== existingNode.nodeId)
|
|
]);
|
|
}
|
|
|
|
// should add node into existing hierarchy
|
|
const cloned = cloneDeep(s.currentNode);
|
|
templateApi(s.hierarchy).constructNode(
|
|
parentNode,
|
|
cloned
|
|
);
|
|
|
|
const newIndexOfchild = child => {
|
|
if(child === cloned) return index;
|
|
const currentIndex = parentNode.children.indexOf(child);
|
|
return currentIndex >= index ? currentIndex + 1 : currentIndex;
|
|
}
|
|
|
|
parentNode.children = chain(parentNode.children, [
|
|
sortBy(newIndexOfchild)
|
|
]);
|
|
|
|
s.currentNodeIsNew = false;
|
|
|
|
savePackage(store, s);
|
|
|
|
return s;
|
|
});
|
|
}
|
|
|
|
const importAppDefinition = store => appDefinition => {
|
|
store.update(s => {
|
|
s.hierarchy = appDefinition.hierarchy;
|
|
s.currentNode = appDefinition.hierarchy.children.length > 0
|
|
? appDefinition.hierarchy.children[0]
|
|
: null;
|
|
s.actions = appDefinition.actions;
|
|
s.triggers = appDefinition.triggers;
|
|
s.currentNodeIsNew = false;
|
|
return s;
|
|
});
|
|
}
|
|
|
|
const deleteCurrentNode = store => () => {
|
|
store.update(s => {
|
|
const nodeToDelete = getNode(s.hierarchy, s.currentNode.nodeId);
|
|
s.currentNode = hierarchyFunctions.isRoot(nodeToDelete.parent())
|
|
? find(n => n != s.currentNode)
|
|
(s.hierarchy.children)
|
|
: nodeToDelete.parent();
|
|
if(hierarchyFunctions.isRecord(nodeToDelete)) {
|
|
nodeToDelete.parent().children = filter(c => c.nodeId !== nodeToDelete.nodeId)
|
|
(nodeToDelete.parent().children);
|
|
} else {
|
|
nodeToDelete.parent().indexes = filter(c => c.nodeId !== nodeToDelete.nodeId)
|
|
(nodeToDelete.parent().indexes);
|
|
}
|
|
s.errors = [];
|
|
savePackage(store, s);
|
|
return s;
|
|
});
|
|
}
|
|
|
|
const saveField = databaseStore => (field) => {
|
|
databaseStore.update(db => {
|
|
db.currentNode.fields = filter(f => f.name !== field.name)
|
|
(db.currentNode.fields);
|
|
|
|
templateApi(db.hierarchy).addField(db.currentNode, field);
|
|
return db;
|
|
});
|
|
}
|
|
|
|
|
|
const deleteField = databaseStore => field => {
|
|
databaseStore.update(db => {
|
|
db.currentNode.fields = filter(f => f.name !== field.name)
|
|
(db.currentNode.fields);
|
|
|
|
return db;
|
|
});
|
|
}
|
|
|
|
|
|
const saveAction = store => (newAction, isNew, oldAction=null) => {
|
|
store.update(s => {
|
|
|
|
const existingAction = isNew
|
|
? null
|
|
: find(a => a.name === oldAction.name)(s.actions);
|
|
|
|
if(existingAction) {
|
|
s.actions = chain(s.actions, [
|
|
map(a => a === existingAction ? newAction : a)
|
|
]);
|
|
} else {
|
|
s.actions.push(newAction);
|
|
}
|
|
savePackage(store, s);
|
|
return s;
|
|
});
|
|
}
|
|
|
|
const deleteAction = store => action => {
|
|
store.update(s => {
|
|
s.actions = filter(a => a.name !== action.name)(s.actions);
|
|
savePackage(store, s);
|
|
return s;
|
|
});
|
|
}
|
|
|
|
const saveTrigger = store => (newTrigger, isNew, oldTrigger=null) => {
|
|
store.update(s => {
|
|
|
|
const existingTrigger = isNew
|
|
? null
|
|
: find(a => a.name === oldTrigger.name)(s.triggers);
|
|
|
|
if(existingTrigger) {
|
|
s.triggers = chain(s.triggers, [
|
|
map(a => a === existingTrigger ? newTrigger : a)
|
|
]);
|
|
} else {
|
|
s.triggers.push(newTrigger);
|
|
}
|
|
savePackage(store, s);
|
|
return s;
|
|
});
|
|
}
|
|
|
|
const deleteTrigger = store => trigger => {
|
|
store.update(s => {
|
|
s.triggers = filter(t => t.name !== trigger.name)(s.triggers);
|
|
return s;
|
|
});
|
|
}
|
|
|
|
const saveLevel = store => (newLevel, isNew, oldLevel=null) => {
|
|
store.update(s => {
|
|
|
|
const existingLevel = isNew
|
|
? null
|
|
: find(a => a.name === oldLevel.name)(s.accessLevels);
|
|
|
|
if(existingLevel) {
|
|
s.accessLevels = chain(s.accessLevels, [
|
|
map(a => a === existingLevel ? newLevel : a)
|
|
]);
|
|
} else {
|
|
s.accessLevels.push(newLevel);
|
|
}
|
|
savePackage(store, s);
|
|
return s;
|
|
});
|
|
}
|
|
|
|
const deleteLevel = store => level => {
|
|
store.update(s => {
|
|
s.accessLevels = filter(t => t.name !== level.name)(s.accessLevels);
|
|
savePackage(store, s);
|
|
return s;
|
|
});
|
|
}
|
|
|
|
const setActiveNav = databaseStore => navName => {
|
|
databaseStore.update(db => {
|
|
db.activeNav = navName;
|
|
return db;
|
|
});
|
|
}
|
|
|
|
const createShadowHierarchy = hierarchy =>
|
|
constructHierarchy(JSON.parse(JSON.stringify(hierarchy)));
|
|
|
|
const savePackage = (store, db) => {
|
|
|
|
const appDefinition = {
|
|
hierarchy:db.hierarchy,
|
|
triggers:db.triggers,
|
|
actions: groupBy("name")(db.actions)
|
|
};
|
|
|
|
const data = {
|
|
appDefinition,
|
|
accessLevels:db.accessLevels
|
|
}
|
|
|
|
fetch(`/_builder/api/${db.appname}/appPackage`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|