forked from tsai/budibase
15 changed files with 358 additions and 351 deletions
@ -0,0 +1,163 @@ |
|||
import api from "../api" |
|||
import { |
|||
filter, |
|||
cloneDeep, |
|||
sortBy, |
|||
find |
|||
} from "lodash/fp" |
|||
import { hierarchy as hierarchyFunctions } from "../../../../core/src" |
|||
import { |
|||
pipe, |
|||
getNode, |
|||
validate, |
|||
constructHierarchy, |
|||
templateApi, |
|||
} from "../../common/core" |
|||
import { store } from "../index"; |
|||
|
|||
export const createShadowHierarchy = hierarchy => |
|||
constructHierarchy(JSON.parse(JSON.stringify(hierarchy))) |
|||
|
|||
export const saveBackend = async state => { |
|||
await api.post(`/_builder/api/${state.appname}/backend`, { |
|||
appDefinition: { |
|||
hierarchy: state.hierarchy, |
|||
actions: state.actions, |
|||
triggers: state.triggers, |
|||
}, |
|||
accessLevels: state.accessLevels, |
|||
}) |
|||
} |
|||
|
|||
export const newRecord = (store, useRoot) => () => { |
|||
store.update(s => { |
|||
s.currentNodeIsNew = true |
|||
const shadowHierarchy = createShadowHierarchy(s.hierarchy) |
|||
const parent = useRoot |
|||
? shadowHierarchy |
|||
: getNode(shadowHierarchy, s.currentNode.nodeId) |
|||
s.errors = [] |
|||
s.currentNode = templateApi(shadowHierarchy).getNewRecordTemplate( |
|||
parent, |
|||
"", |
|||
true |
|||
) |
|||
return s |
|||
}) |
|||
} |
|||
|
|||
export const selectExistingNode = store => nodeId => { |
|||
store.update(s => { |
|||
const shadowHierarchy = createShadowHierarchy(s.hierarchy) |
|||
s.currentNode = getNode(shadowHierarchy, nodeId) |
|||
s.currentNodeIsNew = false |
|||
s.errors = [] |
|||
return s |
|||
}) |
|||
} |
|||
|
|||
export const newIndex = (store, useRoot) => () => { |
|||
store.update(s => { |
|||
s.currentNodeIsNew = true |
|||
s.errors = [] |
|||
const shadowHierarchy = createShadowHierarchy(s.hierarchy) |
|||
const parent = useRoot |
|||
? shadowHierarchy |
|||
: getNode(shadowHierarchy, s.currentNode.nodeId) |
|||
|
|||
s.currentNode = templateApi(shadowHierarchy).getNewIndexTemplate(parent) |
|||
return s |
|||
}) |
|||
} |
|||
|
|||
// TODO: ONLY SEEMS TO BE CALLED BY THE BACKEND
|
|||
export 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 = pipe(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 = pipe(parentNode.children, [sortBy(newIndexOfChild)]) |
|||
|
|||
if (!existingNode && s.currentNode.type === "record") { |
|||
const defaultIndex = templateApi(s.hierarchy).getNewIndexTemplate( |
|||
cloned.parent() |
|||
) |
|||
defaultIndex.name = `all_${cloned.collectionName}` |
|||
defaultIndex.allowedRecordNodeIds = [cloned.nodeId] |
|||
} |
|||
|
|||
s.currentNodeIsNew = false |
|||
|
|||
saveBackend(s) |
|||
|
|||
return s |
|||
}) |
|||
} |
|||
|
|||
export 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 = [] |
|||
saveBackend(s) |
|||
return s |
|||
}) |
|||
} |
|||
|
|||
export 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 |
|||
}) |
|||
} |
|||
|
|||
export const deleteField = databaseStore => field => { |
|||
databaseStore.update(db => { |
|||
db.currentNode.fields = filter(f => f.name !== field.name)( |
|||
db.currentNode.fields |
|||
) |
|||
|
|||
return db |
|||
}) |
|||
} |
|||
@ -1,24 +1,26 @@ |
|||
import api from "../../builderStore/api"; |
|||
import { getNewRecord } from "../../common/core" |
|||
|
|||
export async function deleteRecord(appName, appInstanceId, record) { |
|||
const DELETE_RECORDS_URL = `/_builder/instance/${appName}/${appInstanceId}/api/record/${record.name}/${record.id}` |
|||
export async function deleteRecord(record, { appname, instanceId }) { |
|||
const DELETE_RECORDS_URL = `/_builder/instance/${appname}/${instanceId}/api/record/${record.name}/${record.id}` |
|||
const response = await api.delete({ |
|||
url: DELETE_RECORDS_URL |
|||
}); |
|||
return response; |
|||
} |
|||
|
|||
export async function createNewRecord(record) { |
|||
console.log(record); |
|||
export async function saveRecord(record, { appname, instanceId }) { |
|||
const SAVE_RECORDS_URL = `/_builder/instance/${appname}/${instanceId}/api/record` |
|||
const updatedRecord = getNewRecord(record, "") |
|||
const response = await api.post(SAVE_RECORDS_URL, updatedRecord) |
|||
return response |
|||
} |
|||
|
|||
export async function fetchDataForView(viewName) { |
|||
console.log(viewName); |
|||
// const FETCH_RECORDS_URL = `/_builder/instance/${}/${}/api/listRecords/`
|
|||
export async function fetchDataForView(viewName, { appname, instanceId }) { |
|||
const FETCH_RECORDS_URL = `/_builder/instance/${appname}/${instanceId}/api/listRecords/${viewName}`; |
|||
|
|||
// const response = await api.get({ url: FETCH_RECORDS_URL });
|
|||
|
|||
// console.log(response);
|
|||
|
|||
// GET /_builder/instance/:appname/:instanceid/api/listRecords/contacts/abcd1234/all_deals
|
|||
// TODO: Error handling
|
|||
const response = await api.get(FETCH_RECORDS_URL); |
|||
return await response.json(); |
|||
|
|||
} |
|||
@ -1,60 +1,84 @@ |
|||
<script> |
|||
import { onMount } from "svelte" |
|||
import { store } from "../../../builderStore" |
|||
import Modal from "../../../common/Modal.svelte" |
|||
import ActionButton from "../../../common/ActionButton.svelte" |
|||
import Select from "../../../common/Select.svelte" |
|||
import * as api from "../api" |
|||
|
|||
export let modalOpen = false |
|||
export let model = {} |
|||
export let record |
|||
|
|||
let selectedModel |
|||
|
|||
$: currentAppInfo = { |
|||
instanceId: $store.currentInstanceId, |
|||
appname: $store.appname, |
|||
} |
|||
$: recordFields = record ? Object.keys(record) : [] |
|||
$: models = $store.hierarchy.children |
|||
$: modelFields = selectedModel |
|||
? selectedModel.fields.map(({ name }) => name) |
|||
: [] |
|||
|
|||
const onClosed = () => (modalOpen = false) |
|||
</script> |
|||
|
|||
<Modal {onClosed} bind:isOpen={modalOpen} title={'Record'}> |
|||
<Modal {onClosed} isOpen={modalOpen}> |
|||
<h4 class="budibase__title--4">Create / Edit Record</h4> |
|||
<div class="actions"> |
|||
<form> |
|||
<div class="uk-margin"> |
|||
<label class="uk-form-label" for="form-stacked-text">Text</label> |
|||
<div class="uk-form-controls"> |
|||
<input |
|||
class="uk-input" |
|||
id="form-stacked-text" |
|||
type="text" |
|||
placeholder="Some text..." /> |
|||
{console.log('record', record)} |
|||
{console.log('selectedModel', selectedModel)} |
|||
{console.log('recordFields', recordFields)} |
|||
<form class="uk-form-stacked"> |
|||
{#if !record} |
|||
<div class="uk-margin"> |
|||
<label class="uk-form-label" for="form-stacked-text">Model</label> |
|||
<Select bind:value={selectedModel}> |
|||
{#each models as model} |
|||
<option value={model}>{model.name}</option> |
|||
{/each} |
|||
</Select> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="uk-margin"> |
|||
<label class="uk-form-label" for="form-stacked-select">Select</label> |
|||
<div class="uk-form-controls"> |
|||
<select class="uk-select" id="form-stacked-select"> |
|||
<option>Option 01</option> |
|||
<option>Option 02</option> |
|||
</select> |
|||
{/if} |
|||
{#each modelFields as field} |
|||
<div class="uk-margin"> |
|||
<label class="uk-form-label" for="form-stacked-text">{field}</label> |
|||
<div class="uk-form-controls"> |
|||
<input |
|||
class="uk-input" |
|||
id="form-stacked-text" |
|||
type="text" |
|||
bind:value={selectedModel[field]} /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="uk-margin"> |
|||
<div class="uk-form-label">Radio</div> |
|||
<div class="uk-form-controls"> |
|||
<label> |
|||
<input class="uk-radio" type="radio" name="radio1" /> |
|||
Option 01 |
|||
</label> |
|||
<br /> |
|||
<label> |
|||
<input class="uk-radio" type="radio" name="radio1" /> |
|||
Option 02 |
|||
</label> |
|||
{/each} |
|||
{#each recordFields as field} |
|||
<div class="uk-margin"> |
|||
<label class="uk-form-label" for="form-stacked-text">{field}</label> |
|||
<div class="uk-form-controls"> |
|||
<input |
|||
class="uk-input" |
|||
id="form-stacked-text" |
|||
type="text" |
|||
bind:value={record[field]} /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
{/each} |
|||
</form> |
|||
<ActionButton alert on:click={onClosed}>Cancel</ActionButton> |
|||
<ActionButton |
|||
disabled={false} |
|||
on:click={() => api.createNewRecord(recordInfo)}> |
|||
Save |
|||
</ActionButton> |
|||
<div class="actions"> |
|||
<ActionButton alert on:click={onClosed}>Cancel</ActionButton> |
|||
<ActionButton |
|||
disabled={false} |
|||
on:click={() => api.saveRecord(record || selectedModel, currentAppInfo)}> |
|||
Save |
|||
</ActionButton> |
|||
</div> |
|||
</div> |
|||
</Modal> |
|||
|
|||
<style> |
|||
.actions { |
|||
position: absolute; |
|||
} |
|||
</style> |
|||
|
|||
Loading…
Reference in new issue