enhanced modals

This commit is contained in:
Martin McKeaveney 2020-03-21 20:39:37 +00:00
parent a8fec53ff3
commit 9756574e6f
15 changed files with 176 additions and 106 deletions

View File

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

View File

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

View File

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

View File

@ -19,39 +19,32 @@
</script>
<div class="root">
<ButtonGroup>
<ActionButton color="secondary" grouped on:click={store.saveCurrentNode}>
Save
</ActionButton>
<div class="button-container">
{#if !$store.currentNodeIsNew}
<ActionButton alert grouped on:click={openConfirmDelete}>
<ActionButton alert on:click={deleteCurrentNode}>
Delete
</ActionButton>
{/if}
</ButtonGroup>
<ActionButton color="secondary" on:click={store.saveCurrentNode}>
Save
</ActionButton>
</div>
{#if $store.errors && $store.errors.length > 0}
<ErrorsBox errors={$store.errors} />
{/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>
<style>
.root {
padding: 1.5rem;
display: flex;
background: #fafafa;
width: 100%;
align-items: right;
border-top: 1px solid #ccc;
}
.button-container {
padding: 20px;
}
</style>

View File

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

View File

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

View File

@ -1,5 +1,12 @@
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 }) {
const DELETE_RECORDS_URL = `/_builder/instance/${appname}/${instanceId}/api/record${record.key}`

View File

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

View File

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

View File

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

View File

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

View File

@ -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>

View File

@ -15,22 +15,22 @@
function onClosed() {
backendUiStore.actions.modals.hide()
}
</script>
<Modal {onClosed} isOpen={modalOpen}>
<section>
<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">
<ActionButton on:click={onClosed}>Cancel</ActionButton>
<ActionButton
alert
on:click={async () => {
on:click={async () => {
await api.deleteRecord(record, currentAppInfo)
backendUiStore.actions.records.delete(record)
onClosed();
onClosed()
}}>
Delete
</ActionButton>
</div>
</Modal>
</section>

View File

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

View File

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