Browse Source

enhanced modals

master
Martin McKeaveney 7 years ago
parent
commit
9756574e6f
  1. 77
      packages/builder/src/builderStore/store/backend.js
  2. 4
      packages/builder/src/builderStore/store/index.js
  3. 1
      packages/builder/src/common/Modal.svelte
  4. 35
      packages/builder/src/database/ActionsHeader.svelte
  5. 30
      packages/builder/src/database/DatabaseRoot.svelte
  6. 20
      packages/builder/src/database/ModelDataTable/ModelDataTable.svelte
  7. 9
      packages/builder/src/database/ModelDataTable/api.js
  8. 16
      packages/builder/src/database/ModelDataTable/modals/CreateDatabase.svelte
  9. 7
      packages/builder/src/database/ModelDataTable/modals/CreateEditModel.svelte
  10. 8
      packages/builder/src/database/ModelDataTable/modals/CreateEditRecord.svelte
  11. 4
      packages/builder/src/database/ModelDataTable/modals/CreateEditView.svelte
  12. 27
      packages/builder/src/database/ModelDataTable/modals/CreateUser.svelte
  13. 12
      packages/builder/src/database/ModelDataTable/modals/DeleteRecord.svelte
  14. 2
      packages/builder/src/database/ModelView.svelte
  15. 30
      packages/builder/src/nav/HierarchyRow.svelte

77
packages/builder/src/builderStore/store/backend.js

@ -45,6 +45,11 @@ export const getBackendUiStore = () => {
modals: { modals: {
show: modal => store.update(state => ({ ...state, visibleModal: modal })), show: modal => store.update(state => ({ ...state, visibleModal: modal })),
hide: () => store.update(state => ({ ...state, visibleModal: null })) hide: () => store.update(state => ({ ...state, visibleModal: null }))
},
nodes: {
select: () => {},
update: () => {},
delete: () => {},
} }
} }
@ -52,10 +57,13 @@ export const getBackendUiStore = () => {
}; };
// Store Actions // Store Actions
export const createShadowHierarchy = hierarchy => { export const createShadowHierarchy = hierarchy => constructHierarchy(JSON.parse(JSON.stringify(hierarchy)))
const hi = constructHierarchy(JSON.parse(JSON.stringify(hierarchy)))
console.log(hi) export const createDatabaseForApp = store => appInstance => {
return hi store.update(state => {
state.appInstances.push(appInstance)
return state
})
} }
export const saveBackend = async state => { export const saveBackend = async state => {
@ -98,12 +106,12 @@ export const selectExistingNode = store => nodeId => {
export const newIndex = (store, useRoot) => () => { export const newIndex = (store, useRoot) => () => {
store.update(state => { store.update(state => {
const shadowHierarchy = createShadowHierarchy(state.hierarchy)
state.currentNodeIsNew = true state.currentNodeIsNew = true
state.errors = [] state.errors = []
const shadowHierarchy = createShadowHierarchy(state.hierarchy)
const parent = useRoot const parent = useRoot
? shadowHierarchy ? state.hierarchy
: getNode(shadowHierarchy, state.currentNode.nodeId) : getNode(state.hierarchy, state.currentNode.nodeId)
state.currentNode = templateApi(shadowHierarchy).getNewIndexTemplate(parent) state.currentNode = templateApi(shadowHierarchy).getNewIndexTemplate(parent)
return state return state
@ -114,7 +122,7 @@ export const saveCurrentNode = store => () => {
store.update(state => { store.update(state => {
const errors = validate.node(state.currentNode) const errors = validate.node(state.currentNode)
state.errors = errors state.errors = errors
if (errorstate.length > 0) { if (errors.length > 0) {
return state return state
} }
@ -126,9 +134,10 @@ export const saveCurrentNode = store => () => {
if (existingNode) { if (existingNode) {
// remove existing // remove existing
index = existingNode.parent().children.indexOf(existingNode) index = existingNode.parent().children.indexOf(existingNode)
existingNode.parent().children = pipe(existingNode.parent().children, [ existingNode.parent().children = existingNode.parent().children.filter(c => c.nodeId !== existingNode.nodeId);
filter(c => c.nodeId !== existingNode.nodeId), // existingNode.parent().children = pipe(existingNode.parent().children, [
]) // filter(c => c.nodeId !== existingNode.nodeId),
// ])
} }
// should add node into existing hierarchy // should add node into existing hierarchy
@ -141,7 +150,7 @@ export const saveCurrentNode = store => () => {
return currentIndex >= index ? currentIndex + 1 : currentIndex return currentIndex >= index ? currentIndex + 1 : currentIndex
} }
parentNode.children = pipe(parentNode.children, [sortBy(newIndexOfChild)]) parentNode.children = sortBy(newIndexOfChild, parentNode.children)
if (!existingNode && state.currentNode.type === "record") { if (!existingNode && state.currentNode.type === "record") {
const defaultIndex = templateApi(state.hierarchy).getNewIndexTemplate( const defaultIndex = templateApi(state.hierarchy).getNewIndexTemplate(
@ -162,18 +171,28 @@ export const saveCurrentNode = store => () => {
export const deleteCurrentNode = store => () => { export const deleteCurrentNode = store => () => {
store.update(state => { store.update(state => {
const nodeToDelete = getNode(state.hierarchy, state.currentNode.nodeId) const nodeToDelete = getNode(state.hierarchy, state.currentNode.nodeId)
state.currentNode = hierarchyFunctionstate.isRoot(nodeToDelete.parent()) state.currentNode = hierarchyFunctions.isRoot(nodeToDelete.parent())
? find(n => n != state.currentNode)(state.hierarchy.children) ? find(n => n !== state.currentNode)(state.hierarchy.children)
: nodeToDelete.parent() : nodeToDelete.parent()
if (hierarchyFunctionstate.isRecord(nodeToDelete)) {
nodeToDelete.parent().children = filter( const recordOrIndexKey = hierarchyFunctions.isRecord(nodeToDelete) ? "children" : "indexes";
c => c.nodeId !== nodeToDelete.nodeId
)(nodeToDelete.parent().children) // remove the selected record or index
} else { nodeToDelete.parent()[recordOrIndexKey] = remove(
nodeToDelete.parent().indexes = filter( nodeToDelete.parent()[recordOrIndexKey],
c => c.nodeId !== nodeToDelete.nodeId node => node.nodeId === nodeToDelete.nodeId
)(nodeToDelete.parent().indexes) )
}
// if (hierarchyFunctions.isRecord(nodeToDelete)) {
// nodeToDelete.parent().children = filter(
// c => c.nodeId !== nodeToDelete.nodeId
// )(nodeToDelete.parent().children)
// } else {
// nodeToDelete.parent().indexes = remove(
// nodeToDelete.parent().indexes,
// node => node.nodeId === nodeToDelete.nodeId
// )
// }
state.errors = [] state.errors = []
saveBackend(state) saveBackend(state)
return state return state
@ -202,25 +221,23 @@ export const deleteField = databaseStore => field => {
} }
const incrementAccessLevelsVersion = state => const incrementAccessLevelsVersion = state =>
(state.accessLevelstate.version = (state.accessLevelstate.version || 0) + 1) (state.accessLevels.version = (state.accessLevels.version || 0) + 1)
export const saveLevel = store => (newLevel, isNew, oldLevel = null) => { export const saveLevel = store => (newLevel, isNew, oldLevel = null) => {
store.update(state => { store.update(state => {
const levels = state.accessLevelstate.levels const levels = state.accessLevels.levels
const existingLevel = isNew const existingLevel = isNew
? null ? null
: find(a => a.name === oldLevel.name)(levels) : find(a => a.name === oldLevel.name)(levels)
if (existingLevel) { if (existingLevel) {
state.accessLevelstate.levels = pipe(levels, [ state.accessLevels.levels = levels.map(level => level === existingLevel ? newLevel : level)
map(a => (a === existingLevel ? newLevel : a)),
])
} else { } else {
state.accessLevelstate.levelstate.push(newLevel) state.accessLevels.levels.push(newLevel)
} }
incrementAccessLevelsVersion(s) incrementAccessLevelsVersion(state)
saveBackend(state) saveBackend(state)
return state return state

4
packages/builder/src/builderStore/store/index.js

@ -1,7 +1,6 @@
import { import {
filter, filter,
cloneDeep, cloneDeep,
// sortBy,
map, map,
last, last,
concat, concat,
@ -12,9 +11,7 @@ import {
import { import {
pipe, pipe,
getNode, getNode,
// validate,
constructHierarchy, constructHierarchy,
// templateApi,
} from "../../common/core" } from "../../common/core"
import * as backendStoreActions from "./backend"; import * as backendStoreActions from "./backend";
import { writable } from "svelte/store" import { writable } from "svelte/store"
@ -78,6 +75,7 @@ export const getStore = () => {
store.deleteField = backendStoreActions.deleteField(store) store.deleteField = backendStoreActions.deleteField(store)
store.saveLevel = backendStoreActions.saveLevel(store) store.saveLevel = backendStoreActions.saveLevel(store)
store.deleteLevel = backendStoreActions.deleteLevel(store) store.deleteLevel = backendStoreActions.deleteLevel(store)
store.createDatabaseForApp = backendStoreActions.createDatabaseForApp(store)
store.importAppDefinition = importAppDefinition(store) store.importAppDefinition = importAppDefinition(store)
store.saveAction = saveAction(store) store.saveAction = saveAction(store)

1
packages/builder/src/common/Modal.svelte

@ -24,6 +24,7 @@
} }
} }
} }
</script> </script>
<div bind:this={ukModal} uk-modal {id}> <div bind:this={ukModal} uk-modal {id}>

35
packages/builder/src/database/ActionsHeader.svelte

@ -19,39 +19,32 @@
</script> </script>
<div class="root"> <div class="root">
<ButtonGroup> <div class="button-container">
<ActionButton color="secondary" grouped on:click={store.saveCurrentNode}>
Save
</ActionButton>
{#if !$store.currentNodeIsNew} {#if !$store.currentNodeIsNew}
<ActionButton alert grouped on:click={openConfirmDelete}> <ActionButton alert on:click={deleteCurrentNode}>
Delete Delete
</ActionButton> </ActionButton>
{/if} {/if}
</ButtonGroup>
<ActionButton color="secondary" on:click={store.saveCurrentNode}>
Save
</ActionButton>
</div>
{#if $store.errors && $store.errors.length > 0} {#if $store.errors && $store.errors.length > 0}
<ErrorsBox errors={$store.errors} /> <ErrorsBox errors={$store.errors} />
{/if} {/if}
<Modal onClosed={() => (confirmDelete = false)} bind:isOpen={confirmDelete}>
<span>Are you sure you want to delete {$store.currentNode.name}?</span>
<div class="uk-modal-footer uk-text-right">
<ButtonGroup>
<ActionButton alert on:click={deleteCurrentNode}>Yes</ActionButton>
<ActionButton primary on:click={() => (confirmDelete = false)}>
No
</ActionButton>
</ButtonGroup>
</div>
</Modal>
</div> </div>
<style> <style>
.root { .root {
padding: 1.5rem; display: flex;
background: #fafafa;
width: 100%; width: 100%;
align-items: right; border-top: 1px solid #ccc;
}
.button-container {
padding: 20px;
} }
</style> </style>

30
packages/builder/src/database/DatabaseRoot.svelte

@ -12,34 +12,46 @@
CreateEditRecordModal, CreateEditRecordModal,
CreateEditModelModal, CreateEditModelModal,
CreateEditViewModal, CreateEditViewModal,
CreateDatabaseModal CreateDatabaseModal,
DeleteRecordModal
} from "./ModelDataTable/modals" } from "./ModelDataTable/modals"
let selectedRecord let selectedRecord
function selectRecord(record) { function selectRecord(record) {
selectedRecord = record selectedRecord = record
backendUiStore.actions.modals.show("RECORD")
} }
function onClosed() { function onClosed() {
// backendUiStore.actions.modals.hide() backendUiStore.actions.modals.hide()
} }
$: recordOpen = $backendUiStore.visibleModal === "RECORD" $: recordOpen = $backendUiStore.visibleModal === "RECORD"
$: modelOpen = $backendUiStore.visibleModal === "MODEL" $: modelOpen = $backendUiStore.visibleModal === "MODEL"
$: viewOpen = $backendUiStore.visibleModal === "VIEW" $: viewOpen = $backendUiStore.visibleModal === "VIEW"
$: databaseOpen = $backendUiStore.visibleModal === "DATABASE" $: databaseOpen = $backendUiStore.visibleModal === "DATABASE"
$: deleteRecordOpen = $backendUiStore.visibleModal === "DELETE_RECORD"
// $: recordOpen = $store.currentNode && $store.currentNode.type === 'record' // $: recordOpen = $store.currentNode && $store.currentNode.type === 'record'
// $: viewOpen = $store.currentNode && $store.currentNode.type === 'index' // $: viewOpen = $store.currentNode && $store.currentNode.type === 'index'
</script> </script>
({ console.log($backendUiStore.visibleModal) }) <Modal isOpen={!!$backendUiStore.visibleModal} {onClosed}>
{#if recordOpen}
<CreateEditRecordModal modalOpen={recordOpen} record={selectedRecord} {onClosed} /> <CreateEditRecordModal record={selectedRecord} {onClosed} />
<CreateEditModelModal modalOpen={modelOpen} {onClosed} /> {/if}
<CreateEditViewModal modalOpen={viewOpen} {onClosed} /> {#if modelOpen}
<CreateDatabaseModal modalOpen={databaseOpen} {onClosed} /> <CreateEditModelModal {onClosed} />
{/if}
{#if viewOpen}
<CreateEditViewModal {onClosed} />
{/if}
{#if databaseOpen}
<CreateDatabaseModal {onClosed} />
{/if}
{#if deleteRecordOpen}
<DeleteRecordModal record={selectedRecord} />
{/if}
</Modal>
<div class="root"> <div class="root">

20
packages/builder/src/database/ModelDataTable/ModelDataTable.svelte

@ -38,15 +38,12 @@
} }
onMount(async () => { onMount(async () => {
await fetchRecordsForView(views[0].name, currentAppInfo) if (views.length > 0) {
await fetchRecordsForView(views[0].name, currentAppInfo)
}
}) })
</script> </script>
<DeleteRecordModal
modalOpen={deleteRecordModal}
record={selectedRecord}
/>
<section> <section>
<div class="table-controls"> <div class="table-controls">
<h4 class="budibase__title--3">Shoe database</h4> <h4 class="budibase__title--3">Shoe database</h4>
@ -79,13 +76,16 @@
<div>View</div> <div>View</div>
</li> </li>
<li <li
on:click={() => selectRecord(row)}> on:click={() => {
selectRecord(row)
backendUiStore.actions.modals.show("RECORD")
}}>
<div>Edit</div> <div>Edit</div>
</li> </li>
<li> <li>
<div <div
on:click={() => { on:click={() => {
selectedRecord = row selectRecord(row)
backendUiStore.actions.modals.show("DELETE_RECORD") backendUiStore.actions.modals.show("DELETE_RECORD")
}}> }}>
Delete Delete
@ -112,7 +112,8 @@
table { table {
border: 1px solid #ccc; border: 1px solid #ccc;
background: #fff; background: #fff;
border-radius: 2px; border-radius: 3px;
border-collapse: separate;
} }
thead { thead {
@ -128,6 +129,7 @@
tbody tr { tbody tr {
border-bottom: 1px solid #ccc; border-bottom: 1px solid #ccc;
transition: 0.3s background-color; transition: 0.3s background-color;
color: var(--darkslate);
} }
tbody tr:hover { tbody tr:hover {

9
packages/builder/src/database/ModelDataTable/api.js

@ -1,5 +1,12 @@
import api from "../../builderStore/api"; import api from "../../builderStore/api";
import { getNewRecord } from "../../common/core" import { getNewRecord, getNewInstance } from "../../common/core"
export async function createDatabase(appname, instanceName) {
const CREATE_DATABASE_URL = `/_builder/instance/_master/0/api/record`
const database = getNewInstance(appname, instanceName);
const response = await api.post(CREATE_DATABASE_URL, database);
return await response.json()
}
export async function deleteRecord(record, { appname, instanceId }) { export async function deleteRecord(record, { appname, instanceId }) {
const DELETE_RECORDS_URL = `/_builder/instance/${appname}/${instanceId}/api/record${record.key}` const DELETE_RECORDS_URL = `/_builder/instance/${appname}/${instanceId}/api/record${record.key}`

16
packages/builder/src/database/ModelDataTable/modals/CreateDatabase.svelte

@ -1,17 +1,23 @@
<script> <script>
import Modal from "../../../common/Modal.svelte" import Modal from "../../../common/Modal.svelte"
import { store } from "../../../builderStore"
import ActionButton from "../../../common/ActionButton.svelte" import ActionButton from "../../../common/ActionButton.svelte"
import * as api from "../api" import * as api from "../api"
export let modalOpen = false let databaseName
const onClosed = () => (modalOpen = false) async function createDatabase() {
const response = await api.createDatabase($store.appname, databaseName)
store.createDatabaseForApp(response)
onClosed()
}
</script> </script>
<Modal {onClosed} isOpen={modalOpen}> <section>
CREATE A NEW DATABASE FROM HERE CREATE A NEW DATABASE FROM HERE
<input type="text" bind:value={databaseName} />
<div class="actions"> <div class="actions">
<ActionButton alert on:click={onClosed}>Cancel</ActionButton> <ActionButton alert on:click={onClosed}>Cancel</ActionButton>
<ActionButton disabled={false}>Save</ActionButton> <ActionButton disabled={!databaseName} on:click={createDatabase}>Save</ActionButton>
</div> </div>
</Modal> </section>

7
packages/builder/src/database/ModelDataTable/modals/CreateEditModel.svelte

@ -5,12 +5,9 @@
import ModelView from "../../ModelView.svelte" import ModelView from "../../ModelView.svelte"
import ActionsHeader from "../../ActionsHeader.svelte" import ActionsHeader from "../../ActionsHeader.svelte"
import * as api from "../api" import * as api from "../api"
export let modalOpen = false
export let onClosed
</script> </script>
<Modal {onClosed} isOpen={modalOpen}> <section>
<h4 class="budibase__title--4"> <h4 class="budibase__title--4">
<i class="ri-list-settings-line" /> <i class="ri-list-settings-line" />
Create / Edit Model Create / Edit Model
@ -19,4 +16,4 @@
<ModelView /> <ModelView />
<ActionsHeader /> <ActionsHeader />
</div> </div>
</Modal> </section>

8
packages/builder/src/database/ModelDataTable/modals/CreateEditRecord.svelte

@ -7,7 +7,6 @@
import { getNewRecord } from "../../../common/core" import { getNewRecord } from "../../../common/core"
import * as api from "../api" import * as api from "../api"
export let modalOpen = false
export let record export let record
export let onClosed export let onClosed
@ -22,10 +21,9 @@
$: modelFields = selectedModel $: modelFields = selectedModel
? selectedModel.fields.map(({ name }) => name) ? selectedModel.fields.map(({ name }) => name)
: [] : []
</script> </script>
<Modal {onClosed} isOpen={modalOpen}> <div>
<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 class="uk-form-stacked"> <form class="uk-form-stacked">
@ -80,10 +78,10 @@
</ActionButton> </ActionButton>
</div> </div>
</div> </div>
</Modal> </div>
<style> <style>
.actions { .actions {
position: absolute; /* position: absolute; */
} }
</style> </style>

4
packages/builder/src/database/ModelDataTable/modals/CreateEditView.svelte

@ -12,7 +12,7 @@
</script> </script>
<Modal {onClosed} isOpen={modalOpen}> <section>
<IndexView /> <IndexView />
<ActionsHeader /> <ActionsHeader />
@ -22,4 +22,4 @@
Save Save
</ActionButton> </ActionButton>
</div> --> </div> -->
</Modal> </section>

27
packages/builder/src/database/ModelDataTable/modals/CreateUser.svelte

@ -0,0 +1,27 @@
<script>
import Modal from "../../../common/Modal.svelte"
import { store } from "../../../builderStore"
import ActionButton from "../../../common/ActionButton.svelte"
import * as api from "../api"
export let modalOpen = false
let userName
const onClosed = () => (modalOpen = false)
async function createUser() {
const response = await api.createUser($store.appname, userName)
store.createUserForInstance(response)
onClosed()
}
</script>
<Modal {onClosed} isOpen={modalOpen}>
CREATE A NEW user FROM HERE
<input type="text" bind:value={userName} />
<div class="actions">
<ActionButton alert on:click={onClosed}>Cancel</ActionButton>
<ActionButton disabled={!userName} on:click={createUser}>Save</ActionButton>
</div>
</Modal>

12
packages/builder/src/database/ModelDataTable/modals/DeleteRecord.svelte

@ -15,22 +15,22 @@
function onClosed() { function onClosed() {
backendUiStore.actions.modals.hide() backendUiStore.actions.modals.hide()
} }
</script> </script>
<Modal {onClosed} isOpen={modalOpen}> <section>
<h4 class="budibase__title--4">Delete Record</h4> <h4 class="budibase__title--4">Delete Record</h4>
Are you sure you want to delete this record? All of your data will be permanently removed. This action cannot be undone. Are you sure you want to delete this record? All of your data will be
permanently removed. This action cannot be undone.
<div class="modal-actions"> <div class="modal-actions">
<ActionButton on:click={onClosed}>Cancel</ActionButton> <ActionButton on:click={onClosed}>Cancel</ActionButton>
<ActionButton <ActionButton
alert alert
on:click={async () => { on:click={async () => {
await api.deleteRecord(record, currentAppInfo) await api.deleteRecord(record, currentAppInfo)
backendUiStore.actions.records.delete(record) backendUiStore.actions.records.delete(record)
onClosed(); onClosed()
}}> }}>
Delete Delete
</ActionButton> </ActionButton>
</div> </div>
</Modal> </section>

2
packages/builder/src/database/ModelView.svelte

@ -110,7 +110,7 @@
<div class="uk-form-controls"> <div class="uk-form-controls">
<Select <Select
value={parentRecord} value={parentRecord}
on:change={e => (parentRecord = e.target.value)}> on:change={e => store.newChildRecord()}>
{#each models as model} {#each models as model}
<option value={model}>{model.name}</option> <option value={model}>{model.name}</option>
{/each} {/each}

30
packages/builder/src/nav/HierarchyRow.svelte

@ -3,11 +3,15 @@
import { store, backendUiStore } from "../builderStore" import { store, backendUiStore } from "../builderStore"
import { cloneDeep } from "lodash/fp" import { cloneDeep } from "lodash/fp"
import getIcon from "../common/icon" import getIcon from "../common/icon"
export let level = 0 export let level = 0
export let node export let node
export let type export let type
let navActive = "" let navActive = ""
let expanded = false
$: hasChildren = (node.children || node.indexes) && (node.children.length || node.indexes.length)
const ICON_MAP = { const ICON_MAP = {
index: "ri-eye-line", index: "ri-eye-line",
@ -34,18 +38,26 @@
class:capitalized={type === 'record'} class:capitalized={type === 'record'}
style="padding-left: {20 + level * 20}px" style="padding-left: {20 + level * 20}px"
class:selected={navActive}> class:selected={navActive}>
{#if hasChildren}
<i
class={`ri-arrow-${expanded ? "down" : "right"}-s-line`}
on:click={() => expanded = !expanded}
/>
{/if}
<i class={ICON_MAP[type]} /> <i class={ICON_MAP[type]} />
<span style="margin-left: 1rem">{node.name}</span> <span style="margin-left: 1rem">{node.name}</span>
</div> </div>
{#if node.children} {#if expanded}
{#each node.children as child} {#if node.children}
<svelte:self node={child} level={level + 1} type="record" /> {#each node.children as child}
{/each} <svelte:self node={child} level={level + 1} type="record" />
{/if} {/each}
{#if node.indexes} {/if}
{#each node.indexes as index} {#if node.indexes}
<svelte:self node={index} level={level + 1} type="index" /> {#each node.indexes as index}
{/each} <svelte:self node={index} level={level + 1} type="index" />
{/each}
{/if}
{/if} {/if}
</div> </div>

Loading…
Cancel
Save