enable renaming of records by using IDs

This commit is contained in:
Martin McKeaveney 2020-06-23 23:26:54 +01:00
parent 83da838fcd
commit e1b88e6620
17 changed files with 82 additions and 52 deletions

View File

@ -1,5 +1,6 @@
import { writable } from "svelte/store" import { writable } from "svelte/store"
import { cloneDeep } from "lodash/fp" import { cloneDeep } from "lodash/fp"
import { uuid } from "builderStore/uuid"
import api from "../api" import api from "../api"
export const getBackendUiStore = () => { export const getBackendUiStore = () => {
@ -61,14 +62,15 @@ export const getBackendUiStore = () => {
save: async ({ model }) => { save: async ({ model }) => {
const updatedModel = cloneDeep(model) const updatedModel = cloneDeep(model)
// TODO: refactor // // TODO: refactor
for (let key in updatedModel.schema) { // for (let key in updatedModel.schema) {
const field = updatedModel.schema[key] // const field = updatedModel.schema[key]
if (field.name && field.name !== key) { // // TODO: use IDs
updatedModel.schema[field.name] = field // if (field.name && field.name !== key) {
delete updatedModel.schema[key] // updatedModel.schema[field.name] = field
} // delete updatedModel.schema[key]
} // }
// }
const SAVE_MODEL_URL = `/api/models` const SAVE_MODEL_URL = `/api/models`
const response = await api.post(SAVE_MODEL_URL, updatedModel) const response = await api.post(SAVE_MODEL_URL, updatedModel)
@ -86,8 +88,6 @@ export const getBackendUiStore = () => {
state.models = state.models state.models = state.models
} }
// TODO: fetch models
store.actions.models.select(savedModel) store.actions.models.select(savedModel)
return state return state
}) })
@ -98,13 +98,13 @@ export const getBackendUiStore = () => {
state.draftModel.schema = {} state.draftModel.schema = {}
} }
const id = uuid()
state.draftModel.schema = { state.draftModel.schema = {
...state.draftModel.schema, ...state.draftModel.schema,
[field.name]: field, [id]: field,
} }
state.selectedField = id
state.selectedField = field.name
state.tabs.NAVIGATION_PANEL = "NAVIGATE" state.tabs.NAVIGATION_PANEL = "NAVIGATE"
return state return state

View File

@ -18,6 +18,7 @@
"_id", "_id",
"_rev", "_rev",
$backendUiStore.selectedModel.name, $backendUiStore.selectedModel.name,
modelId
] ]
async function fetchRecords() { async function fetchRecords() {
@ -26,10 +27,6 @@
records = await response.json() records = await response.json()
} }
onMount(() => {
fetchRecords()
})
function linkRecord(id) { function linkRecord(id) {
if (linkedRecords.has(id)) { if (linkedRecords.has(id)) {
linkedRecords.delete(id) linkedRecords.delete(id)
@ -39,6 +36,10 @@
linkedRecords = linkedRecords linkedRecords = linkedRecords
} }
onMount(() => {
fetchRecords()
})
</script> </script>
<section> <section>

View File

@ -15,6 +15,7 @@
"type", "type",
"_id", "_id",
"_rev", "_rev",
$backendUiStore.selectedModel._id,
$backendUiStore.selectedModel.name, $backendUiStore.selectedModel.name,
] ]

View File

@ -48,9 +48,6 @@
if ($backendUiStore.selectedView) { if ($backendUiStore.selectedView) {
api.fetchDataForView($backendUiStore.selectedView).then(records => { api.fetchDataForView($backendUiStore.selectedView).then(records => {
data = records || [] data = records || []
headers = Object.keys($backendUiStore.selectedModel.schema).filter(
key => !INTERNAL_HEADERS.includes(key)
)
}) })
} }
} }
@ -62,6 +59,12 @@
) )
: [] : []
$: headers = Object.keys($backendUiStore.selectedModel.schema).filter(
id => !INTERNAL_HEADERS.includes(id)
)
$: schema = $backendUiStore.selectedModel.schema
const createNewRecord = () => { const createNewRecord = () => {
open( open(
CreateEditRecordModal, CreateEditRecordModal,
@ -94,7 +97,7 @@
<tr> <tr>
<th>Edit</th> <th>Edit</th>
{#each headers as header} {#each headers as header}
<th>{header}</th> <th>{$backendUiStore.selectedModel.schema[header].name}</th>
{/each} {/each}
</tr> </tr>
</thead> </thead>
@ -129,9 +132,11 @@
</td> </td>
{#each headers as header} {#each headers as header}
<td> <td>
{#if Array.isArray(row[header])} {#if schema[header].type === "link"}
<LinkedRecord {header} ids={row[header]} /> <LinkedRecord header={schema[header].name} ids={row[header]} />
{:else}{row[header] || 0}{/if} {:else}
{row[header]}
{/if}
</td> </td>
{/each} {/each}
</tr> </tr>

View File

@ -79,13 +79,13 @@
{#if meta.type === 'link'} {#if meta.type === 'link'}
<LinkedRecordSelector <LinkedRecordSelector
bind:linked={record[key]} bind:linked={record[key]}
linkName={key} linkName={meta.name}
modelId={meta.modelId} /> modelId={meta.modelId} />
{:else} {:else}
<RecordFieldControl <RecordFieldControl
type={determineInputType(meta)} type={determineInputType(meta)}
options={determineOptions(meta)} options={determineOptions(meta)}
label={key} label={meta.name}
bind:value={record[key]} /> bind:value={record[key]} />
{/if} {/if}
</div> </div>
@ -101,7 +101,7 @@
header { header {
margin-bottom: 40px; margin-bottom: 40px;
display: grid; display: grid;
grid-gap: 5px; grid-gap: 20px;
grid-template-columns: 40px 1fr; grid-template-columns: 40px 1fr;
align-items: center; align-items: center;
} }
@ -115,7 +115,6 @@
background: var(--secondary); background: var(--secondary);
color: var(--ink); color: var(--ink);
font-size: 20px; font-size: 20px;
margin-right: 20px;
border-radius: 3px; border-radius: 3px;
} }

View File

@ -1,5 +1,6 @@
<script> <script>
import { backendUiStore } from "builderStore" import { backendUiStore } from "builderStore"
import { uuid } from "builderStore/uuid"
import { fade } from "svelte/transition" import { fade } from "svelte/transition"
import { FIELDS, BLOCKS, MODELS } from "constants/backend" import { FIELDS, BLOCKS, MODELS } from "constants/backend"
import Block from "components/common/Block.svelte" import Block from "components/common/Block.svelte"
@ -11,9 +12,16 @@
function createModel(model) { function createModel(model) {
const { schema, ...rest } = $backendUiStore.selectedModel const { schema, ...rest } = $backendUiStore.selectedModel
const newModel = { ...model, schema: {} }
// TODO: could be better
for (let key in model.schema) {
newModel.schema[uuid()] = model.schema[key]
}
backendUiStore.actions.models.save({ backendUiStore.actions.models.save({
model: { model: {
...model, ...newModel,
...rest, ...rest,
}, },
}) })

View File

@ -23,12 +23,12 @@
$: selectedTab = $backendUiStore.tabs.NAVIGATION_PANEL $: selectedTab = $backendUiStore.tabs.NAVIGATION_PANEL
function selectModel(model, fieldName) { function selectModel(model, fieldId) {
backendUiStore.actions.models.select(model) backendUiStore.actions.models.select(model)
if (fieldName) { if (fieldId) {
backendUiStore.update(state => { backendUiStore.update(state => {
state.selectedField = fieldName state.selectedField = fieldId
return state return state
}) })
} }
@ -38,6 +38,7 @@
backendUiStore.update(state => { backendUiStore.update(state => {
state.selectedModel = {} state.selectedModel = {}
state.draftModel = { schema: {} } state.draftModel = { schema: {} }
state.tabs.SETUP_PANEL = "SETUP"
return state return state
}) })
} }
@ -63,13 +64,13 @@
on:click={() => selectModel(model)} /> on:click={() => selectModel(model)} />
{#if model._id === $backendUiStore.selectedModel._id} {#if model._id === $backendUiStore.selectedModel._id}
<div in:slide> <div in:slide>
{#each Object.keys(model.schema) as field} {#each Object.keys(model.schema) as fieldId}
<ListItem <ListItem
selected={model._id === $backendUiStore.selectedModel._id && field === $backendUiStore.selectedField} selected={model._id === $backendUiStore.selectedModel._id && fieldId === $backendUiStore.selectedField}
indented indented
icon="ri-layout-column-fill" icon="ri-layout-column-fill"
title={field} title={model.schema[fieldId].name}
on:click={() => selectModel(model, field)} /> on:click={() => selectModel(model, fieldId)} />
{/each} {/each}
</div> </div>
{/if} {/if}

View File

@ -22,6 +22,18 @@
field.constraints && field.constraints &&
field.constraints.presence && field.constraints.presence &&
!constraints.presence.allowEmpty !constraints.presence.allowEmpty
function attachModelIdToSchema(evt) {
const { draftModel } = $backendUiStore
if ($backendUiStore.selectedField !== evt.target.value) {
delete draftModel.schema[$backendUiStore.selectedField]
draftModel.schema[evt.target.value] = field
backendUiStore.update(state => {
state.selectedField = evt.target.value
return state
})
}
}
</script> </script>
<div class="info"> <div class="info">
@ -66,10 +78,12 @@
{:else if field.type === 'link'} {:else if field.type === 'link'}
<div class="field"> <div class="field">
<label>Link</label> <label>Link</label>
<select class="budibase__input" bind:value={field.modelId}> <select class="budibase__input" bind:value={field.modelId} on:change={attachModelIdToSchema}>
<option value={''} /> <option value={''} />
{#each $backendUiStore.models as model} {#each $backendUiStore.models as model}
<option value={model._id}>{model.name}</option> {#if model._id !== $backendUiStore.draftModel._id}
<option value={model._id}>{model.name}</option>
{/if}
{/each} {/each}
</select> </select>
</div> </div>

View File

@ -43,6 +43,8 @@
const response = await api.delete(DELETE_MODEL_URL) const response = await api.delete(DELETE_MODEL_URL)
backendUiStore.update(state => { backendUiStore.update(state => {
state.selectedView = null state.selectedView = null
state.selectedModel = {}
state.draftModel = {}
state.models = state.models.filter(({ _id }) => _id !== model._id) state.models = state.models.filter(({ _id }) => _id !== model._id)
notifier.danger(`${model.name} deleted successfully.`) notifier.danger(`${model.name} deleted successfully.`)
return state return state
@ -64,7 +66,7 @@
{#if selectedTab === 'SETUP'} {#if selectedTab === 'SETUP'}
{#if $backendUiStore.selectedField} {#if $backendUiStore.selectedField}
<ModelFieldEditor /> <ModelFieldEditor />
{:else} {:else if $backendUiStore.draftModel.schema}
<div class="titled-input"> <div class="titled-input">
<header>Name</header> <header>Name</header>
<input <input

View File

@ -45,7 +45,7 @@ exports.create = async function(ctx) {
} }
const appId = newid() const appId = newid()
// insert an appId -> clientId lookup // insert an appId -> clientId lookup
const masterDb = new CouchDB("clientAppLookup") const masterDb = new CouchDB("client_app_lookup")
await masterDb.put({ await masterDb.put({
_id: appId, _id: appId,
@ -132,7 +132,7 @@ const createEmptyAppPackage = async (ctx, app) => {
} }
const lookupClientId = async appId => { const lookupClientId = async appId => {
const masterDb = new CouchDB("clientAppLookup") const masterDb = new CouchDB("client_app_lookup")
const { clientId } = await masterDb.get(appId) const { clientId } = await masterDb.get(appId)
return clientId return clientId
} }

View File

@ -11,7 +11,7 @@ exports.authenticate = async ctx => {
if (!username) ctx.throw(400, "Username Required.") if (!username) ctx.throw(400, "Username Required.")
if (!password) ctx.throw(400, "Password Required") if (!password) ctx.throw(400, "Password Required")
const masterDb = new CouchDB("clientAppLookup") const masterDb = new CouchDB("client_app_lookup")
const { clientId } = await masterDb.get(ctx.user.appId) const { clientId } = await masterDb.get(ctx.user.appId)

View File

@ -7,7 +7,7 @@ const {
} = require("../../utilities/budibaseDir") } = require("../../utilities/budibaseDir")
exports.fetchAppComponentDefinitions = async function(ctx) { exports.fetchAppComponentDefinitions = async function(ctx) {
const masterDb = new CouchDB("clientAppLookup") const masterDb = new CouchDB("client_app_lookup")
const { clientId } = await masterDb.get(ctx.params.appId) const { clientId } = await masterDb.get(ctx.params.appId)
const db = new CouchDB(ClientDb.name(clientId)) const db = new CouchDB(ClientDb.name(clientId))
const app = await db.get(ctx.params.appId) const app = await db.get(ctx.params.appId)

View File

@ -8,7 +8,7 @@ exports.create = async function(ctx) {
const appShortId = appId.substring(0, 7) const appShortId = appId.substring(0, 7)
const instanceId = `inst_${appShortId}_${newid()}` const instanceId = `inst_${appShortId}_${newid()}`
const masterDb = new CouchDB("clientAppLookup") const masterDb = new CouchDB("client_app_lookup")
const { clientId } = await masterDb.get(appId) const { clientId } = await masterDb.get(appId)
const db = new CouchDB(instanceId) const db = new CouchDB(instanceId)

View File

@ -16,7 +16,7 @@ exports.find = async function(ctx) {
ctx.body = model ctx.body = model
} }
exports.create = async function(ctx) { exports.save = async function(ctx) {
const db = new CouchDB(ctx.user.instanceId) const db = new CouchDB(ctx.user.instanceId)
const modelToSave = { const modelToSave = {
type: "model", type: "model",
@ -33,7 +33,7 @@ exports.create = async function(ctx) {
if (schema[key].type === "link") { if (schema[key].type === "link") {
// create the link field in the other model // create the link field in the other model
const linkedModel = await db.get(schema[key].modelId) const linkedModel = await db.get(schema[key].modelId)
linkedModel.schema[modelToSave.name] = { linkedModel.schema[modelToSave._id] = {
name: modelToSave.name, name: modelToSave.name,
type: "link", type: "link",
modelId: modelToSave._id, modelId: modelToSave._id,

View File

@ -56,8 +56,8 @@ exports.save = async function(ctx) {
const doc = row.doc const doc = row.doc
return { return {
...doc, ...doc,
[model.name]: doc[model.name] [model._id]: doc[model._id]
? [...doc[model.name], record._id] ? [...doc[model._id], record._id]
: [record._id], : [record._id],
} }
}) })

View File

@ -41,7 +41,7 @@ exports.create = async function(ctx) {
const response = await database.post(user) const response = await database.post(user)
const masterDb = new CouchDB("clientAppLookup") const masterDb = new CouchDB("client_app_lookup")
const { clientId } = await masterDb.get(appId) const { clientId } = await masterDb.get(appId)
// the clientDB needs to store a map of users against the app // the clientDB needs to store a map of users against the app

View File

@ -12,8 +12,7 @@ router
authorized(READ_MODEL, ctx => ctx.params.id), authorized(READ_MODEL, ctx => ctx.params.id),
modelController.find modelController.find
) )
.post("/api/models", authorized(BUILDER), modelController.create) .post("/api/models", authorized(BUILDER), modelController.save)
// .patch("/api/:instanceId/models", controller.update)
.delete( .delete(
"/api/models/:modelId/:revId", "/api/models/:modelId/:revId",
authorized(BUILDER), authorized(BUILDER),