Add custom modal for creating and editing users
This commit is contained in:
parent
74f5197865
commit
b70512ada2
|
@ -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>
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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 => {
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue