Add custom modal for creating and editing users

This commit is contained in:
Andrew Kingston 2020-12-07 19:05:39 +00:00
parent 74f5197865
commit b70512ada2
8 changed files with 159 additions and 25 deletions

View File

@ -8,10 +8,13 @@
import * as api from "./api" import * as api from "./api"
import Table from "./Table.svelte" import Table from "./Table.svelte"
import { TableNames } from "constants" import { TableNames } from "constants"
import CreateEditUser from "./modals/CreateEditUser.svelte"
import CreateEditRow from "./modals/CreateEditRow.svelte"
let data = [] let data = []
let loading = false let loading = false
$: isUsersTable = $backendUiStore.selectedTable?._id === TableNames.USERS
$: title = $backendUiStore.selectedTable.name $: title = $backendUiStore.selectedTable.name
$: schema = $backendUiStore.selectedTable.schema $: schema = $backendUiStore.selectedTable.schema
$: tableView = { $: tableView = {
@ -31,14 +34,21 @@
} }
</script> </script>
<Table {title} {schema} {data} allowEditing={true} {loading}> <Table
{title}
{schema}
tableId={$backendUiStore.selectedTable?._id}
{data}
allowEditing={true}
{loading}>
<CreateColumnButton /> <CreateColumnButton />
{#if schema && Object.keys(schema).length > 0} {#if schema && Object.keys(schema).length > 0}
<CreateRowButton /> <CreateRowButton
modalContentComponent={isUsersTable ? CreateEditUser : CreateEditRow} />
<CreateViewButton /> <CreateViewButton />
<ExportButton view={tableView} /> <ExportButton view={tableView} />
{/if} {/if}
{#if $backendUiStore.selectedTable?._id === TableNames.USERS} {#if isUsersTable}
<EditRolesButton /> <EditRolesButton />
{/if} {/if}
</Table> </Table>

View File

@ -7,20 +7,16 @@
Toggle, Toggle,
RichText, RichText,
} from "@budibase/bbui" } from "@budibase/bbui"
import { backendUiStore } from "builderStore"
import { TableNames } from "constants"
import Dropzone from "components/common/Dropzone.svelte" import Dropzone from "components/common/Dropzone.svelte"
import { capitalise } from "../../../helpers" import { capitalise } from "../../../helpers"
import LinkedRowSelector from "components/common/LinkedRowSelector.svelte" import LinkedRowSelector from "components/common/LinkedRowSelector.svelte"
export let meta export let meta
export let creating
export let value = meta.type === "boolean" ? false : "" export let value = meta.type === "boolean" ? false : ""
export let readonly
$: type = meta.type $: type = meta.type
$: label = capitalise(meta.name) $: label = capitalise(meta.name)
$: editingUser =
!creating && $backendUiStore.selectedTable?._id === TableNames.USERS
</script> </script>
{#if type === 'options'} {#if type === 'options'}
@ -53,5 +49,5 @@
data-cy="{meta.name}-input" data-cy="{meta.name}-input"
{type} {type}
bind:value bind:value
disabled={editingUser} /> disabled={readonly} />
{/if} {/if}

View File

@ -7,10 +7,15 @@
import { notifier } from "builderStore/store/notifications" import { notifier } from "builderStore/store/notifications"
import Spinner from "components/common/Spinner.svelte" import Spinner from "components/common/Spinner.svelte"
import DeleteRowsButton from "./buttons/DeleteRowsButton.svelte" import DeleteRowsButton from "./buttons/DeleteRowsButton.svelte"
import { getRenderer, editRowRenderer } from "./cells/cellRenderers" import {
getRenderer,
editRowRenderer,
userRowRenderer,
} from "./cells/cellRenderers"
import TableLoadingOverlay from "./TableLoadingOverlay" import TableLoadingOverlay from "./TableLoadingOverlay"
import TableHeader from "./TableHeader" import TableHeader from "./TableHeader"
import "@budibase/svelte-ag-grid/dist/index.css" import "@budibase/svelte-ag-grid/dist/index.css"
import { TableNames } from "constants"
export let schema = {} export let schema = {}
export let data = [] export let data = []
@ -42,6 +47,14 @@
animateRows: true, animateRows: true,
} }
$: isUsersTable = tableId === TableNames.USERS
$: {
if (isUsersTable) {
schema.username.displayFieldName = "Username"
schema.roleId.displayFieldName = "Role"
}
}
$: { $: {
let result = [] let result = []
if (allowEditing) { if (allowEditing) {
@ -57,12 +70,12 @@
suppressMenu: true, suppressMenu: true,
minWidth: 114, minWidth: 114,
width: 114, width: 114,
cellRenderer: editRowRenderer, cellRenderer: isUsersTable ? userRowRenderer : editRowRenderer,
}, },
] ]
} }
Object.keys(schema || {}).forEach((key, idx) => { Object.entries(schema || {}).forEach(([key, value]) => {
result.push({ result.push({
headerCheckboxSelection: false, headerCheckboxSelection: false,
headerComponent: TableHeader, headerComponent: TableHeader,
@ -70,7 +83,7 @@
field: schema[key], field: schema[key],
editable: allowEditing, editable: allowEditing,
}, },
headerName: key, headerName: value.displayFieldName || key,
field: key, field: key,
sortable: true, sortable: true,
cellRenderer: getRenderer(schema[key], true), cellRenderer: getRenderer(schema[key], true),

View File

@ -1,6 +1,8 @@
<script> <script>
import { TextButton as Button, Icon, Modal } from "@budibase/bbui" import { TextButton as Button, Icon, Modal } from "@budibase/bbui"
import CreateEditRowModal from "../modals/CreateEditRowModal.svelte" import CreateEditRow from "../modals/CreateEditRow.svelte"
export let modalContentComponent = CreateEditRow
let modal let modal
</script> </script>
@ -12,5 +14,5 @@
</Button> </Button>
</div> </div>
<Modal bind:this={modal}> <Modal bind:this={modal}>
<CreateEditRowModal /> <svelte:component this={modalContentComponent} />
</Modal> </Modal>

View File

@ -1,5 +1,6 @@
import AttachmentList from "./AttachmentCell.svelte" import AttachmentList from "./AttachmentCell.svelte"
import EditRow from "../modals/EditRow.svelte" import EditRow from "../modals/EditRow.svelte"
import CreateEditUser from "../modals/CreateEditUser.svelte"
import DeleteRow from "../modals/DeleteRow.svelte" import DeleteRow from "../modals/DeleteRow.svelte"
import RelationshipDisplay from "./RelationshipCell.svelte" import RelationshipDisplay from "./RelationshipCell.svelte"
@ -45,6 +46,23 @@ export function editRowRenderer(params) {
return container return container
} }
export function userRowRenderer(params) {
const container = document.createElement("div")
container.style.height = "100%"
container.style.display = "flex"
container.style.alignItems = "center"
new EditRow({
target: container,
props: {
row: params.data,
modalContentComponent: CreateEditUser,
},
})
return container
}
/* eslint-disable no-unused-vars */ /* eslint-disable no-unused-vars */
function attachmentRenderer(options, constraints, editable) { function attachmentRenderer(options, constraints, editable) {
return params => { return params => {

View File

@ -1,6 +1,5 @@
<script> <script>
import { backendUiStore } from "builderStore" import { backendUiStore } from "builderStore"
import { TableNames } from "constants"
import { notifier } from "builderStore/store/notifications" import { notifier } from "builderStore/store/notifications"
import RowFieldControl from "../RowFieldControl.svelte" import RowFieldControl from "../RowFieldControl.svelte"
import * as api from "../api" import * as api from "../api"
@ -40,15 +39,9 @@
confirmText={creating ? 'Create Row' : 'Save Row'} confirmText={creating ? 'Create Row' : 'Save Row'}
onConfirm={saveRow}> onConfirm={saveRow}>
<ErrorsBox {errors} /> <ErrorsBox {errors} />
{#if creating && table._id === TableNames.USERS}
<RowFieldControl
{creating}
meta={{ name: 'password', type: 'password' }}
bind:value={row.password} />
{/if}
{#each tableSchema as [key, meta]} {#each tableSchema as [key, meta]}
<div> <div>
<RowFieldControl {meta} bind:value={row[key]} {creating} /> <RowFieldControl {meta} bind:value={row[key]} />
</div> </div>
{/each} {/each}
</ModalContent> </ModalContent>

View File

@ -0,0 +1,101 @@
<script>
import { onMount } from "svelte"
import { backendUiStore } from "builderStore"
import { notifier } from "builderStore/store/notifications"
import RowFieldControl from "../RowFieldControl.svelte"
import * as backendApi from "../api"
import builderApi from "builderStore/api"
import { ModalContent, Select } from "@budibase/bbui"
import ErrorsBox from "components/common/ErrorsBox.svelte"
export let row = {}
let errors = []
let roles = []
let rolesLoaded = false
$: creating = row?._id == null
$: table = row.tableId
? $backendUiStore.tables.find(table => table._id === row?.tableId)
: $backendUiStore.selectedTable
$: tableSchema = getUserSchema(table)
$: customSchemaKeys = getCustomSchemaKeys(tableSchema)
const getUserSchema = table => {
let schema = table?.schema ?? {}
if (schema.username) {
schema.username.name = "Username"
}
return schema
}
const getCustomSchemaKeys = schema => {
let customSchema = { ...schema }
delete customSchema["username"]
delete customSchema["roleId"]
return Object.entries(customSchema)
}
const saveRow = async () => {
const rowResponse = await backendApi.saveRow(
{ ...row, tableId: table._id },
table._id
)
if (rowResponse.errors) {
if (Array.isArray(rowResponse.errors)) {
errors = rowResponse.errors.map(error => ({ message: error }))
} else {
errors = Object.entries(rowResponse.errors)
.map(([key, error]) => ({ dataPath: key, message: error }))
.flat()
}
return false
}
notifier.success("User saved successfully.")
backendUiStore.actions.rows.save(rowResponse)
}
const fetchRoles = async () => {
const rolesResponse = await builderApi.get("/api/roles")
roles = await rolesResponse.json()
rolesLoaded = true
}
onMount(fetchRoles)
</script>
<ModalContent
title={creating ? 'Create User' : 'Edit User'}
confirmText={creating ? 'Create User' : 'Save User'}
onConfirm={saveRow}>
<ErrorsBox {errors} />
<RowFieldControl
meta={{ ...tableSchema.username, name: 'Username' }}
bind:value={row.username}
readonly={!creating} />
{#if creating}
<RowFieldControl
meta={{ name: 'password', type: 'password' }}
bind:value={row.password} />
{/if}
<!-- Defer rendering this select until roles load, otherwise the initial
selection is always undefined -->
{#if rolesLoaded}
<Select
thin
secondary
label="Role"
data-cy="roleId-select"
bind:value={row.roleId}>
<option value="">Choose an option</option>
{#each roles as role}
<option value={role._id}>{role.name}</option>
{/each}
</Select>
{/if}
{#each customSchemaKeys as [key, meta]}
<RowFieldControl {meta} bind:value={row[key]} {creating} />
{/each}
</ModalContent>

View File

@ -1,8 +1,9 @@
<script> <script>
import { Modal, Button } from "@budibase/bbui" import { Modal, Button } from "@budibase/bbui"
import CreateEditRowModal from "../modals/CreateEditRowModal.svelte" import CreateEditRow from "../modals/CreateEditRow.svelte"
export let row export let row
export let modalContentComponent = CreateEditRow
let modal let modal
@ -14,5 +15,5 @@
<Button data-cy="edit-row" secondary small on:click={showModal}>Edit</Button> <Button data-cy="edit-row" secondary small on:click={showModal}>Edit</Button>
<Modal bind:this={modal}> <Modal bind:this={modal}>
<CreateEditRowModal {row} /> <svelte:component this={modalContentComponent} {row} />
</Modal> </Modal>