add new models and fields, delete models
This commit is contained in:
parent
374a6b4bc6
commit
5f8d6bbfe4
|
@ -77,7 +77,8 @@
|
||||||
|
|
||||||
.budibase__input {
|
.budibase__input {
|
||||||
height: 35px;
|
height: 35px;
|
||||||
width: 220px;
|
width: 100%;
|
||||||
|
max-width: 220px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
border: 1px solid var(--grey-dark);
|
border: 1px solid var(--grey-dark);
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|
|
@ -25,9 +25,7 @@ export const getBackendUiStore = () => {
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
state.selectedDatabase = db
|
state.selectedDatabase = db
|
||||||
if (models && models.length > 0) {
|
if (models && models.length > 0) {
|
||||||
state.selectedModel = models[0]
|
store.actions.models.select(models[0]);
|
||||||
state.draftModel = models[0]
|
|
||||||
state.selectedView = `all_${models[0]._id}`
|
|
||||||
}
|
}
|
||||||
state.breadcrumbs = [db.name]
|
state.breadcrumbs = [db.name]
|
||||||
state.models = models
|
state.models = models
|
||||||
|
@ -56,14 +54,25 @@ export const getBackendUiStore = () => {
|
||||||
models: {
|
models: {
|
||||||
select: model => store.update(state => {
|
select: model => store.update(state => {
|
||||||
state.selectedModel = model;
|
state.selectedModel = model;
|
||||||
// TODO: prevent pointing to same obj
|
|
||||||
state.draftModel = cloneDeep(model);
|
state.draftModel = cloneDeep(model);
|
||||||
state.selectedField = null
|
state.selectedField = ""
|
||||||
|
state.selectedView = `all_${model._id}`
|
||||||
return state;
|
return state;
|
||||||
}),
|
}),
|
||||||
save: async ({ instanceId, model }) => {
|
save: async ({ instanceId, model }) => {
|
||||||
|
const updatedModel = cloneDeep(model);
|
||||||
|
|
||||||
|
// TODO: refactor
|
||||||
|
for (let key in updatedModel.schema) {
|
||||||
|
const field = updatedModel.schema[key]
|
||||||
|
if (field.name && field.name !== key) {
|
||||||
|
updatedModel.schema[field.name] = field
|
||||||
|
delete updatedModel.schema[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const SAVE_MODEL_URL = `/api/${instanceId}/models`
|
const SAVE_MODEL_URL = `/api/${instanceId}/models`
|
||||||
const response = await api.post(SAVE_MODEL_URL, model)
|
const response = await api.post(SAVE_MODEL_URL, updatedModel)
|
||||||
const savedModel = await response.json()
|
const savedModel = await response.json()
|
||||||
|
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
|
@ -76,9 +85,7 @@ export const getBackendUiStore = () => {
|
||||||
state.models = state.models
|
state.models = state.models
|
||||||
}
|
}
|
||||||
|
|
||||||
state.selectedModel = savedModel
|
store.actions.models.select(savedModel)
|
||||||
state.draftModel = savedModel
|
|
||||||
state.selectedView = `all_${savedModel._id}`
|
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -93,11 +100,11 @@ export const getBackendUiStore = () => {
|
||||||
[field.name]: field
|
[field.name]: field
|
||||||
}
|
}
|
||||||
|
|
||||||
state.selectedField = field
|
state.selectedField = field.name
|
||||||
|
|
||||||
return state
|
return state
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
views: {
|
views: {
|
||||||
select: view =>
|
select: view =>
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
<script>
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
import { backendUiStore } from "builderStore"
|
||||||
|
import api from "builderStore/api"
|
||||||
|
|
||||||
|
export let modelId
|
||||||
|
export let linkedRecords
|
||||||
|
|
||||||
|
let records = []
|
||||||
|
|
||||||
|
async function fetchRecords() {
|
||||||
|
const FETCH_RECORDS_URL = `/api/${$backendUiStore.selectedDatabase._id}/${modelId}/records`
|
||||||
|
const response = await api.get(FETCH_RECORDS_URL)
|
||||||
|
records = await response.json()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
fetchRecords()
|
||||||
|
})
|
||||||
|
|
||||||
|
function linkRecord(record) {
|
||||||
|
linkedRecords.push(record);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
{#each records as record}
|
||||||
|
<div class="linked-record" on:click={linkRecord}>
|
||||||
|
<h3>{record.name}</h3>
|
||||||
|
<div class="fields">
|
||||||
|
{#each Object.keys(record) as key}
|
||||||
|
<div class="field">
|
||||||
|
<span>{key}</span>
|
||||||
|
<p>{record[key]}</p>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
section {
|
||||||
|
background: var(--grey);
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fields {
|
||||||
|
padding: 20px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
|
grid-gap: 20px;
|
||||||
|
background: var(--white);
|
||||||
|
border: 1px solid var(--grey);
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field span {
|
||||||
|
color: var(--ink-lighter);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field p {
|
||||||
|
color: var(--ink);
|
||||||
|
font-size: 14px;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -68,10 +68,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$: paginatedData = data.slice(
|
$: paginatedData = data ? data.slice(
|
||||||
currentPage * ITEMS_PER_PAGE,
|
currentPage * ITEMS_PER_PAGE,
|
||||||
currentPage * ITEMS_PER_PAGE + ITEMS_PER_PAGE
|
currentPage * ITEMS_PER_PAGE + ITEMS_PER_PAGE
|
||||||
)
|
) : []
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if (views.length) {
|
if (views.length) {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
import { store, backendUiStore } from "builderStore"
|
import { store, backendUiStore } from "builderStore"
|
||||||
import { compose, map, get, flatten } from "lodash/fp"
|
import { compose, map, get, flatten } from "lodash/fp"
|
||||||
import ActionButton from "components/common/ActionButton.svelte"
|
import ActionButton from "components/common/ActionButton.svelte"
|
||||||
|
import LinkedRecordSelector from "components/common/LinkedRecordSelector.svelte"
|
||||||
import Select from "components/common/Select.svelte"
|
import Select from "components/common/Select.svelte"
|
||||||
import RecordFieldControl from "./RecordFieldControl.svelte"
|
import RecordFieldControl from "./RecordFieldControl.svelte"
|
||||||
import * as api from "../api"
|
import * as api from "../api"
|
||||||
|
@ -73,11 +74,15 @@
|
||||||
<form on:submit|preventDefault class="uk-form-stacked">
|
<form on:submit|preventDefault class="uk-form-stacked">
|
||||||
{#each modelSchema as [key, meta]}
|
{#each modelSchema as [key, meta]}
|
||||||
<div class="uk-margin">
|
<div class="uk-margin">
|
||||||
<RecordFieldControl
|
{#if meta.type === "link"}
|
||||||
type={determineInputType(meta)}
|
<LinkedRecordSelector modelId={meta.modelId} />
|
||||||
options={determineOptions(meta)}
|
{:else}
|
||||||
label={key}
|
<RecordFieldControl
|
||||||
bind:value={record[key]} />
|
type={determineInputType(meta)}
|
||||||
|
options={determineOptions(meta)}
|
||||||
|
label={key}
|
||||||
|
bind:value={record[key]} />
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.indented {
|
.indented {
|
||||||
margin-left: 20px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div {
|
div {
|
||||||
|
@ -21,11 +21,11 @@
|
||||||
width: 260px;
|
width: 260px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
font-weight: bold;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
transition: 0.3s background-color;
|
transition: 0.3s background-color;
|
||||||
color: var(--ink);
|
color: var(--ink);
|
||||||
|
font-weight: 500;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
const { open, close } = getContext("simple-modal")
|
const { open, close } = getContext("simple-modal")
|
||||||
|
|
||||||
const HEADINGS = [
|
let HEADINGS = [
|
||||||
{
|
{
|
||||||
title: "Navigate",
|
title: "Navigate",
|
||||||
key: "NAVIGATE",
|
key: "NAVIGATE",
|
||||||
|
@ -28,9 +28,9 @@
|
||||||
backendUiStore.actions.models.select(model)
|
backendUiStore.actions.models.select(model)
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectField(field) {
|
function selectField(fieldName) {
|
||||||
backendUiStore.update(state => {
|
backendUiStore.update(state => {
|
||||||
state.selectedField = field
|
state.selectedField = fieldName
|
||||||
return state
|
return state
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,6 @@
|
||||||
state.draftModel = { schema: {} }
|
state.draftModel = { schema: {} }
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
$goto(`./database/${$backendUiStore.selectedDatabase._id}/newmodel`)
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -51,7 +50,7 @@
|
||||||
<div class="components-list-container">
|
<div class="components-list-container">
|
||||||
<Switcher headings={HEADINGS} bind:value={selectedTab}>
|
<Switcher headings={HEADINGS} bind:value={selectedTab}>
|
||||||
{#if selectedTab === 'NAVIGATE'}
|
{#if selectedTab === 'NAVIGATE'}
|
||||||
<Button secondary wide on:click={setupForNewModel}>Create New Model</Button>
|
<Button secondary wide on:click={setupForNewModel}>Create New Model</Button>
|
||||||
<div class="hierarchy-items-container">
|
<div class="hierarchy-items-container">
|
||||||
{#each $backendUiStore.models as model}
|
{#each $backendUiStore.models as model}
|
||||||
<ListItem
|
<ListItem
|
||||||
|
@ -61,10 +60,11 @@
|
||||||
on:click={() => selectModel(model)} />
|
on:click={() => selectModel(model)} />
|
||||||
{#each Object.keys(model.schema) as field}
|
{#each Object.keys(model.schema) as field}
|
||||||
<ListItem
|
<ListItem
|
||||||
|
selected={field === $backendUiStore.selectedField}
|
||||||
indented
|
indented
|
||||||
icon="ri-layout-column-fill"
|
icon="ri-layout-column-fill"
|
||||||
title={field}
|
title={field}
|
||||||
on:click={() => selectField({ name: field, ...model.schema[field] })} />
|
on:click={() => selectField(field)} />
|
||||||
{/each}
|
{/each}
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -14,12 +14,11 @@
|
||||||
|
|
||||||
const FIELD_TYPES = ["string", "number", "boolean", "link"]
|
const FIELD_TYPES = ["string", "number", "boolean", "link"]
|
||||||
|
|
||||||
export let field
|
let field = {}
|
||||||
|
|
||||||
$: type = field.type
|
$: field = $backendUiStore.draftModel.schema[$backendUiStore.selectedField] || {}
|
||||||
$: constraints = field.constraints
|
|
||||||
$: required =
|
$: required =
|
||||||
constraints && constraints.presence && !constraints.presence.allowEmpty
|
field.constraints && field.constraints.presence && !constraints.presence.allowEmpty
|
||||||
|
|
||||||
const save = () => {
|
const save = () => {
|
||||||
backendUiStore.actions.models.save({
|
backendUiStore.actions.models.save({
|
||||||
|
@ -31,24 +30,24 @@
|
||||||
|
|
||||||
<form on:submit|preventDefault class="uk-form-stacked">
|
<form on:submit|preventDefault class="uk-form-stacked">
|
||||||
<Textbox label="Name" bind:text={field.name} />
|
<Textbox label="Name" bind:text={field.name} />
|
||||||
<Dropdown label="Type" bind:selected={type} options={FIELD_TYPES} />
|
<Dropdown label="Type" bind:selected={field.type} options={FIELD_TYPES} />
|
||||||
|
|
||||||
<Checkbox label="Required" bind:checked={required} />
|
<Checkbox label="Required" bind:checked={required} />
|
||||||
|
|
||||||
{#if type === 'string'}
|
{#if field.type === 'string'}
|
||||||
<NumberBox label="Max Length" bind:value={constraints.length.maximum} />
|
<NumberBox label="Max Length" bind:value={field.constraints.length.maximum} />
|
||||||
<ValuesList label="Categories" bind:values={constraints.inclusion} />
|
<ValuesList label="Categories" bind:values={field.constraints.inclusion} />
|
||||||
{:else if type === 'datetime'}
|
{:else if field.type === 'datetime'}
|
||||||
<DatePicker label="Min Value" bind:value={constraints.datetime.earliest} />
|
<DatePicker label="Min Value" bind:value={field.constraints.datetime.earliest} />
|
||||||
<DatePicker label="Max Value" bind:value={constraints.datetime.latest} />
|
<DatePicker label="Max Value" bind:value={field.constraints.datetime.latest} />
|
||||||
{:else if type === 'number'}
|
{:else if field.type === 'number'}
|
||||||
<NumberBox
|
<NumberBox
|
||||||
label="Min Value"
|
label="Min Value"
|
||||||
bind:value={constraints.numericality.greaterThanOrEqualTo} />
|
bind:value={field.constraints.numericality.greaterThanOrEqualTo} />
|
||||||
<NumberBox
|
<NumberBox
|
||||||
label="Max Value"
|
label="Max Value"
|
||||||
bind:value={constraints.numericality.lessThanOrEqualTo} />
|
bind:value={field.constraints.numericality.lessThanOrEqualTo} />
|
||||||
{:else if type === 'link'}
|
{:else if field.type === 'link'}
|
||||||
<select class="budibase__input" bind:value={field.modelId}>
|
<select class="budibase__input" bind:value={field.modelId}>
|
||||||
<option value={''} />
|
<option value={''} />
|
||||||
{#each $backendUiStore.models as model}
|
{#each $backendUiStore.models as model}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext, onMount } from "svelte"
|
import { getContext, onMount } from "svelte"
|
||||||
import { Button, Switcher } from "@budibase/bbui"
|
import { Button, Switcher } from "@budibase/bbui"
|
||||||
|
import { notifier } from "@beyonk/svelte-notifications"
|
||||||
import { store, backendUiStore } from "builderStore"
|
import { store, backendUiStore } from "builderStore"
|
||||||
import api from "builderStore/api"
|
import api from "builderStore/api"
|
||||||
import FieldView from "./FieldView.svelte";
|
import FieldView from "./FieldView.svelte";
|
||||||
|
@ -27,14 +28,19 @@
|
||||||
$: edited = $backendUiStore.draftModel.name !== $backendUiStore.selectedModel.name
|
$: edited = $backendUiStore.draftModel.name !== $backendUiStore.selectedModel.name
|
||||||
|
|
||||||
async function deleteModel() {
|
async function deleteModel() {
|
||||||
backendUiStore.update(async state => {
|
const modelToDelete = $backendUiStore.selectedModel
|
||||||
const modelToDelete = state.selectedModel
|
if ($backendUiStore.selectedField) {
|
||||||
const DELETE_MODEL_URL = `/api/${state.selectedDatabase._id}/models/${modelToDelete._id}/${modelToDelete._rev}`
|
delete modelToDelete[$backendUiStore.selectedField]
|
||||||
const response = await api.delete(DELETE_MODEL_URL)
|
}
|
||||||
|
|
||||||
|
const DELETE_MODEL_URL = `/api/${$backendUiStore.selectedDatabase._id}/models/${modelToDelete._id}/${modelToDelete._rev}`
|
||||||
|
const response = await api.delete(DELETE_MODEL_URL)
|
||||||
|
backendUiStore.update(state => {
|
||||||
|
state.selectedView = null
|
||||||
state.models = state.models.filter(
|
state.models = state.models.filter(
|
||||||
model => model._id !== modelToDelete._id
|
model => model._id !== modelToDelete._id
|
||||||
)
|
)
|
||||||
state.selectedView = {}
|
notifier.danger(`${modelToDelete.name} deleted successfully.`)
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -44,6 +50,7 @@
|
||||||
instanceId: $backendUiStore.selectedDatabase._id,
|
instanceId: $backendUiStore.selectedDatabase._id,
|
||||||
model: $backendUiStore.draftModel
|
model: $backendUiStore.draftModel
|
||||||
})
|
})
|
||||||
|
notifier.success("Success! Your changes have been saved. Please continue on with your greatness.");
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -51,7 +58,7 @@
|
||||||
<Switcher headings={ITEMS} bind:value={selectedTab}>
|
<Switcher headings={ITEMS} bind:value={selectedTab}>
|
||||||
{#if selectedTab === 'SETUP'}
|
{#if selectedTab === 'SETUP'}
|
||||||
{#if $backendUiStore.selectedField}
|
{#if $backendUiStore.selectedField}
|
||||||
<FieldView field={$backendUiStore.selectedField} />
|
<FieldView />
|
||||||
{:else}
|
{:else}
|
||||||
<div class="titled-input">
|
<div class="titled-input">
|
||||||
<header>Name</header>
|
<header>Name</header>
|
||||||
|
@ -64,7 +71,6 @@
|
||||||
<header>Import Data</header>
|
<header>Import Data</header>
|
||||||
<Button wide secondary>Import CSV</Button>
|
<Button wide secondary>Import CSV</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
attention
|
attention
|
||||||
wide
|
wide
|
||||||
|
@ -82,6 +88,10 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
header {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
.items-root {
|
.items-root {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -79,7 +79,104 @@ export const FIELDS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BLOCKS = {
|
export const BLOCKS = {
|
||||||
|
NAME: {
|
||||||
|
name: "Name",
|
||||||
|
icon: "ri-text",
|
||||||
|
type: "string",
|
||||||
|
constraints: {
|
||||||
|
type: "string",
|
||||||
|
length: {},
|
||||||
|
presence: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PHONE_NUMBER: {
|
||||||
|
name: "Phone Number",
|
||||||
|
icon: "ri-number-1",
|
||||||
|
type: "number",
|
||||||
|
constraints: {
|
||||||
|
type: "number",
|
||||||
|
presence: false,
|
||||||
|
numericality: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ACTIVE: {
|
||||||
|
name: "Active",
|
||||||
|
icon: "ri-toggle-line",
|
||||||
|
type: "boolean",
|
||||||
|
constraints: {
|
||||||
|
type: "boolean",
|
||||||
|
presence: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PRIORITY: {
|
||||||
|
name: "Options",
|
||||||
|
icon: "ri-list-check-2",
|
||||||
|
type: "options",
|
||||||
|
constraints: {
|
||||||
|
type: "string",
|
||||||
|
presence: false,
|
||||||
|
inclusion: [
|
||||||
|
"low",
|
||||||
|
"medium",
|
||||||
|
"high"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
END_DATE: {
|
||||||
|
name: "End Date",
|
||||||
|
icon: "ri-calendar-event-fill",
|
||||||
|
type: "datetime",
|
||||||
|
constraints: {
|
||||||
|
type: "date",
|
||||||
|
datetime: {},
|
||||||
|
presence: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
AVATAR: {
|
||||||
|
name: "Avatar",
|
||||||
|
icon: "ri-image-line",
|
||||||
|
type: "image",
|
||||||
|
constraints: {
|
||||||
|
type: "string",
|
||||||
|
presence: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PDF: {
|
||||||
|
name: "PDF",
|
||||||
|
icon: "ri-file-line",
|
||||||
|
type: "file",
|
||||||
|
constraints: {
|
||||||
|
type: "string",
|
||||||
|
presence: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DATA_LINK: {
|
||||||
|
name: "Data Links",
|
||||||
|
icon: "ri-link",
|
||||||
|
type: "link",
|
||||||
|
modelId: null,
|
||||||
|
constraints: {
|
||||||
|
type: "array",
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TABLES = {}
|
// TODO: Needs more thought, need to come up with the constraints etc for each one
|
||||||
|
export const MODELS = {
|
||||||
|
CONTACTS: {
|
||||||
|
icon: "ri-link",
|
||||||
|
name: "Contacts",
|
||||||
|
schema: {
|
||||||
|
Name: BLOCKS.NAME,
|
||||||
|
"Phone Number": BLOCKS.PHONE_NUMBER
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RECIPES: {
|
||||||
|
icon: "ri-link",
|
||||||
|
name: "Recipes",
|
||||||
|
schema: {
|
||||||
|
Name: BLOCKS.NAME,
|
||||||
|
"Phone Number": BLOCKS.PHONE_NUMBER
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -92,7 +92,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
html, body {
|
html, body {
|
||||||
display: grid;
|
|
||||||
font-family: var(--fontnormal);
|
font-family: var(--fontnormal);
|
||||||
color: var(--secondary80);
|
color: var(--secondary80);
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
|
@ -19,11 +19,17 @@
|
||||||
height: 90px;
|
height: 90px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
color: var(--ink);
|
color: var(--ink);
|
||||||
padding: 20px;
|
font-weight: 500;
|
||||||
|
padding: 15px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
transition: 0.3s transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
span {
|
span {
|
||||||
|
@ -32,6 +38,7 @@
|
||||||
|
|
||||||
div:hover {
|
div:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
transform: scale(1.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.primary {
|
.primary {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
|
import NewModel from "./NewModel.svelte"
|
||||||
import ModelDataTable from "components/database/ModelDataTable"
|
import ModelDataTable from "components/database/ModelDataTable"
|
||||||
import { store, backendUiStore } from "builderStore"
|
import { store, backendUiStore } from "builderStore"
|
||||||
import ActionButton from "components/common/ActionButton.svelte"
|
import ActionButton from "components/common/ActionButton.svelte"
|
||||||
|
@ -33,7 +34,9 @@
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{#if $backendUiStore.selectedDatabase._id && $backendUiStore.selectedModel.name}
|
{#if $backendUiStore.selectedModel.schema && Object.keys($backendUiStore.selectedModel.schema).length === 0}
|
||||||
|
<NewModel />
|
||||||
|
{:else if $backendUiStore.selectedDatabase._id && $backendUiStore.selectedModel.name}
|
||||||
<ModelDataTable />
|
<ModelDataTable />
|
||||||
{:else}
|
{:else}
|
||||||
<i style="color: var(--grey-dark)">
|
<i style="color: var(--grey-dark)">
|
||||||
|
|
|
@ -1,31 +1,70 @@
|
||||||
<script>
|
<script>
|
||||||
import { FIELDS } from "constants/backend";
|
import { backendUiStore } from "builderStore";
|
||||||
|
import { FIELDS, BLOCKS, MODELS } from "constants/backend";
|
||||||
import Block from "./Block.svelte";
|
import Block from "./Block.svelte";
|
||||||
|
|
||||||
|
function addNewField(field) {
|
||||||
|
backendUiStore.actions.models.addField(field);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<header>
|
<header>
|
||||||
<h2>Create New Table</h2>
|
<h2>Create New Table</h2>
|
||||||
<span>Before you can view your table, you need to set it up.</span>
|
<p>Before you can view your table, you need to set it up.</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="block-row">
|
<div class="block-row">
|
||||||
<h4>Fields</h4>
|
<span class="block-row-title">Fields</span>
|
||||||
|
<p>Blocks are pre-made fields and help you build your table quicker.</p>
|
||||||
<div class="blocks">
|
<div class="blocks">
|
||||||
{#each Object.values(FIELDS) as field}
|
{#each Object.values(FIELDS) as field}
|
||||||
<Block primary title={field.name} icon={field.icon} />
|
<Block primary title={field.name} icon={field.icon} on:click={() => addNewField(field)} />
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- TODO: More block rows -->
|
<div class="block-row">
|
||||||
|
<span class="block-row-title">Blocks</span>
|
||||||
|
<p>Blocks are pre-made fields and help you build your table quicker.</p>
|
||||||
|
<div class="blocks">
|
||||||
|
{#each Object.values(BLOCKS) as field}
|
||||||
|
<Block secondary title={field.name} icon={field.icon} />
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="block-row">
|
||||||
|
<span class="block-row-title">Models</span>
|
||||||
|
<p>Blocks are pre-made fields and help you build your table quicker.</p>
|
||||||
|
<div class="blocks">
|
||||||
|
{#each Object.values(MODELS) as model}
|
||||||
|
<Block tertiary title={model.name} icon={model.icon} />
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
section {
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-row-title {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.block-row {
|
.block-row {
|
||||||
|
@ -35,6 +74,6 @@
|
||||||
.block-row .blocks {
|
.block-row .blocks {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-auto-flow: column;
|
grid-auto-flow: column;
|
||||||
grid-gap: 10px;
|
grid-gap: 20px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
Loading…
Reference in New Issue