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 api from "../../builderStore/api"; |
||||
|
import { getNewRecord } from "../../common/core" |
||||
|
|
||||
export async function deleteRecord(appName, appInstanceId, record) { |
export async function deleteRecord(record, { appname, instanceId }) { |
||||
const DELETE_RECORDS_URL = `/_builder/instance/${appName}/${appInstanceId}/api/record/${record.name}/${record.id}` |
const DELETE_RECORDS_URL = `/_builder/instance/${appname}/${instanceId}/api/record/${record.name}/${record.id}` |
||||
const response = await api.delete({ |
const response = await api.delete({ |
||||
url: DELETE_RECORDS_URL |
url: DELETE_RECORDS_URL |
||||
}); |
}); |
||||
|
return response; |
||||
} |
} |
||||
|
|
||||
export async function createNewRecord(record) { |
export async function saveRecord(record, { appname, instanceId }) { |
||||
console.log(record); |
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) { |
export async function fetchDataForView(viewName, { appname, instanceId }) { |
||||
console.log(viewName); |
const FETCH_RECORDS_URL = `/_builder/instance/${appname}/${instanceId}/api/listRecords/${viewName}`; |
||||
// const FETCH_RECORDS_URL = `/_builder/instance/${}/${}/api/listRecords/`
|
|
||||
|
|
||||
// const response = await api.get({ url: FETCH_RECORDS_URL });
|
// TODO: Error handling
|
||||
|
const response = await api.get(FETCH_RECORDS_URL); |
||||
// console.log(response);
|
return await response.json(); |
||||
|
|
||||
// GET /_builder/instance/:appname/:instanceid/api/listRecords/contacts/abcd1234/all_deals
|
|
||||
|
|
||||
} |
} |
||||
@ -1,60 +1,84 @@ |
|||||
<script> |
<script> |
||||
|
import { onMount } from "svelte" |
||||
|
import { store } from "../../../builderStore" |
||||
import Modal from "../../../common/Modal.svelte" |
import Modal from "../../../common/Modal.svelte" |
||||
import ActionButton from "../../../common/ActionButton.svelte" |
import ActionButton from "../../../common/ActionButton.svelte" |
||||
|
import Select from "../../../common/Select.svelte" |
||||
import * as api from "../api" |
import * as api from "../api" |
||||
|
|
||||
export let modalOpen = false |
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) |
const onClosed = () => (modalOpen = false) |
||||
</script> |
</script> |
||||
|
|
||||
<Modal {onClosed} bind:isOpen={modalOpen} title={'Record'}> |
<Modal {onClosed} isOpen={modalOpen}> |
||||
<h4 class="budibase__title--4">Create / Edit Record</h4> |
<h4 class="budibase__title--4">Create / Edit Record</h4> |
||||
<div class="actions"> |
<div class="actions"> |
||||
<form> |
{console.log('record', record)} |
||||
<div class="uk-margin"> |
{console.log('selectedModel', selectedModel)} |
||||
<label class="uk-form-label" for="form-stacked-text">Text</label> |
{console.log('recordFields', recordFields)} |
||||
<div class="uk-form-controls"> |
<form class="uk-form-stacked"> |
||||
<input |
{#if !record} |
||||
class="uk-input" |
<div class="uk-margin"> |
||||
id="form-stacked-text" |
<label class="uk-form-label" for="form-stacked-text">Model</label> |
||||
type="text" |
<Select bind:value={selectedModel}> |
||||
placeholder="Some text..." /> |
{#each models as model} |
||||
|
<option value={model}>{model.name}</option> |
||||
|
{/each} |
||||
|
</Select> |
||||
</div> |
</div> |
||||
</div> |
{/if} |
||||
|
{#each modelFields as field} |
||||
<div class="uk-margin"> |
<div class="uk-margin"> |
||||
<label class="uk-form-label" for="form-stacked-select">Select</label> |
<label class="uk-form-label" for="form-stacked-text">{field}</label> |
||||
<div class="uk-form-controls"> |
<div class="uk-form-controls"> |
||||
<select class="uk-select" id="form-stacked-select"> |
<input |
||||
<option>Option 01</option> |
class="uk-input" |
||||
<option>Option 02</option> |
id="form-stacked-text" |
||||
</select> |
type="text" |
||||
|
bind:value={selectedModel[field]} /> |
||||
|
</div> |
||||
</div> |
</div> |
||||
</div> |
{/each} |
||||
|
{#each recordFields as field} |
||||
<div class="uk-margin"> |
<div class="uk-margin"> |
||||
<div class="uk-form-label">Radio</div> |
<label class="uk-form-label" for="form-stacked-text">{field}</label> |
||||
<div class="uk-form-controls"> |
<div class="uk-form-controls"> |
||||
<label> |
<input |
||||
<input class="uk-radio" type="radio" name="radio1" /> |
class="uk-input" |
||||
Option 01 |
id="form-stacked-text" |
||||
</label> |
type="text" |
||||
<br /> |
bind:value={record[field]} /> |
||||
<label> |
</div> |
||||
<input class="uk-radio" type="radio" name="radio1" /> |
|
||||
Option 02 |
|
||||
</label> |
|
||||
</div> |
</div> |
||||
</div> |
{/each} |
||||
|
|
||||
</form> |
</form> |
||||
<ActionButton alert on:click={onClosed}>Cancel</ActionButton> |
<div class="actions"> |
||||
<ActionButton |
<ActionButton alert on:click={onClosed}>Cancel</ActionButton> |
||||
disabled={false} |
<ActionButton |
||||
on:click={() => api.createNewRecord(recordInfo)}> |
disabled={false} |
||||
Save |
on:click={() => api.saveRecord(record || selectedModel, currentAppInfo)}> |
||||
</ActionButton> |
Save |
||||
|
</ActionButton> |
||||
|
</div> |
||||
</div> |
</div> |
||||
</Modal> |
</Modal> |
||||
|
|
||||
|
<style> |
||||
|
.actions { |
||||
|
position: absolute; |
||||
|
} |
||||
|
</style> |
||||
|
|||||
Loading…
Reference in new issue