moves the datasources store to it's separate store
This commit is contained in:
parent
98607ca1bd
commit
5b08b31fd6
|
@ -20,9 +20,8 @@ export const getBackendUiStore = () => {
|
||||||
reset: () => store.set({ ...INITIAL_BACKEND_UI_STATE }),
|
reset: () => store.set({ ...INITIAL_BACKEND_UI_STATE }),
|
||||||
database: {
|
database: {
|
||||||
select: async db => {
|
select: async db => {
|
||||||
const [tables, datasources, queries, integrations] = await Promise.all([
|
const [tables, queries, integrations] = await Promise.all([
|
||||||
api.get(`/api/tables`).then(r => r.json()),
|
api.get(`/api/tables`).then(r => r.json()),
|
||||||
api.get(`/api/datasources`).then(r => r.json()),
|
|
||||||
api.get(`/api/queries`).then(r => r.json()),
|
api.get(`/api/queries`).then(r => r.json()),
|
||||||
api.get("/api/integrations").then(r => r.json()),
|
api.get("/api/integrations").then(r => r.json()),
|
||||||
])
|
])
|
||||||
|
@ -30,7 +29,6 @@ export const getBackendUiStore = () => {
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
state.selectedDatabase = db
|
state.selectedDatabase = db
|
||||||
state.tables = tables
|
state.tables = tables
|
||||||
state.datasources = datasources
|
|
||||||
state.queries = queries
|
state.queries = queries
|
||||||
state.integrations = integrations
|
state.integrations = integrations
|
||||||
return state
|
return state
|
||||||
|
@ -49,56 +47,6 @@ export const getBackendUiStore = () => {
|
||||||
return state
|
return state
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
datasources: {
|
|
||||||
fetch: async () => {
|
|
||||||
const response = await api.get(`/api/datasources`)
|
|
||||||
const json = await response.json()
|
|
||||||
store.update(state => {
|
|
||||||
state.datasources = json
|
|
||||||
return state
|
|
||||||
})
|
|
||||||
return json
|
|
||||||
},
|
|
||||||
select: async datasourceId => {
|
|
||||||
store.update(state => {
|
|
||||||
state.selectedDatasourceId = datasourceId
|
|
||||||
state.selectedQueryId = null
|
|
||||||
return state
|
|
||||||
})
|
|
||||||
},
|
|
||||||
save: async datasource => {
|
|
||||||
const response = await api.post("/api/datasources", datasource)
|
|
||||||
const json = await response.json()
|
|
||||||
store.update(state => {
|
|
||||||
const currentIdx = state.datasources.findIndex(
|
|
||||||
ds => ds._id === json._id
|
|
||||||
)
|
|
||||||
|
|
||||||
if (currentIdx >= 0) {
|
|
||||||
state.datasources.splice(currentIdx, 1, json)
|
|
||||||
} else {
|
|
||||||
state.datasources.push(json)
|
|
||||||
}
|
|
||||||
|
|
||||||
state.datasources = state.datasources
|
|
||||||
state.selectedDatasourceId = json._id
|
|
||||||
return state
|
|
||||||
})
|
|
||||||
return json
|
|
||||||
},
|
|
||||||
delete: async datasource => {
|
|
||||||
await api.delete(
|
|
||||||
`/api/datasources/${datasource._id}/${datasource._rev}`
|
|
||||||
)
|
|
||||||
store.update(state => {
|
|
||||||
state.datasources = state.datasources.filter(
|
|
||||||
existing => existing._id !== datasource._id
|
|
||||||
)
|
|
||||||
state.selectedDatasourceId = null
|
|
||||||
return state
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
queries: {
|
queries: {
|
||||||
fetch: async () => {
|
fetch: async () => {
|
||||||
const response = await api.get(`/api/queries`)
|
const response = await api.get(`/api/queries`)
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
import { writable } from "svelte/store"
|
||||||
|
import api from "../../api"
|
||||||
|
|
||||||
|
function createDatasourcesStore() {
|
||||||
|
const { subscribe, update, set } = writable({
|
||||||
|
sources: [],
|
||||||
|
selected: null,
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
subscribe,
|
||||||
|
set,
|
||||||
|
update,
|
||||||
|
fetch: async () => {
|
||||||
|
const response = await api.get(`/api/datasources`)
|
||||||
|
const json = await response.json()
|
||||||
|
update(state => ({ ...state, sources: json }))
|
||||||
|
return json
|
||||||
|
},
|
||||||
|
select: async datasourceId => {
|
||||||
|
update(state => ({ ...state, selected: datasourceId }))
|
||||||
|
},
|
||||||
|
save: async datasource => {
|
||||||
|
const response = await api.post("/api/datasources", datasource)
|
||||||
|
const json = await response.json()
|
||||||
|
|
||||||
|
update(state => {
|
||||||
|
const currentIdx = state.sources.findIndex(ds => ds._id === json._id)
|
||||||
|
|
||||||
|
const sources = state.sources
|
||||||
|
|
||||||
|
if (currentIdx >= 0) {
|
||||||
|
sources.splice(currentIdx, 1, json)
|
||||||
|
} else {
|
||||||
|
sources.push(json)
|
||||||
|
}
|
||||||
|
|
||||||
|
return { sources, selected: json._id }
|
||||||
|
})
|
||||||
|
return json
|
||||||
|
},
|
||||||
|
delete: async datasource => {
|
||||||
|
await api.delete(`/api/datasources/${datasource._id}/${datasource._rev}`)
|
||||||
|
update(state => {
|
||||||
|
const sources = state.sources.filter(
|
||||||
|
existing => existing._id !== datasource._id
|
||||||
|
)
|
||||||
|
return { sources, selected: null }
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const datasources = createDatasourcesStore()
|
|
@ -1 +1,3 @@
|
||||||
export { permissions } from "./permissions"
|
export { permissions } from "./permissions"
|
||||||
|
export { roles } from "./roles"
|
||||||
|
export { datasources } from "./datasources"
|
||||||
|
|
|
@ -9,6 +9,9 @@ import {
|
||||||
selectedComponent,
|
selectedComponent,
|
||||||
selectedAccessRole,
|
selectedAccessRole,
|
||||||
} from "builderStore"
|
} from "builderStore"
|
||||||
|
// Backendstores
|
||||||
|
import {datasources} from 'builderStore/store/backend/'
|
||||||
|
|
||||||
import { fetchComponentLibDefinitions } from "../loadComponentLibraries"
|
import { fetchComponentLibDefinitions } from "../loadComponentLibraries"
|
||||||
import api from "../api"
|
import api from "../api"
|
||||||
import { FrontendTypes } from "constants"
|
import { FrontendTypes } from "constants"
|
||||||
|
@ -57,6 +60,13 @@ export const getFrontendStore = () => {
|
||||||
appInstance: application.instance,
|
appInstance: application.instance,
|
||||||
}))
|
}))
|
||||||
await hostingStore.actions.fetch()
|
await hostingStore.actions.fetch()
|
||||||
|
|
||||||
|
// Initialise backend stores
|
||||||
|
const [sources] = await Promise.all([
|
||||||
|
api.get(`/api/datasources`).then(r => r.json()),
|
||||||
|
])
|
||||||
|
datasources.set({sources, selected: null})
|
||||||
|
|
||||||
await backendUiStore.actions.database.select(application.instance)
|
await backendUiStore.actions.database.select(application.instance)
|
||||||
},
|
},
|
||||||
routing: {
|
routing: {
|
||||||
|
|
|
@ -2,13 +2,14 @@
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import { goto } from "@sveltech/routify"
|
import { goto } from "@sveltech/routify"
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
|
import { datasources } from 'builderStore/store/backend/'
|
||||||
import EditDatasourcePopover from "./popovers/EditDatasourcePopover.svelte"
|
import EditDatasourcePopover from "./popovers/EditDatasourcePopover.svelte"
|
||||||
import EditQueryPopover from "./popovers/EditQueryPopover.svelte"
|
import EditQueryPopover from "./popovers/EditQueryPopover.svelte"
|
||||||
import NavItem from "components/common/NavItem.svelte"
|
import NavItem from "components/common/NavItem.svelte"
|
||||||
import ICONS from "./icons"
|
import ICONS from "./icons"
|
||||||
|
|
||||||
function selectDatasource(datasource) {
|
function selectDatasource(datasource) {
|
||||||
backendUiStore.actions.datasources.select(datasource._id)
|
datasources.select(datasource._id)
|
||||||
$goto(`./datasource/${datasource._id}`)
|
$goto(`./datasource/${datasource._id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,18 +22,18 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
backendUiStore.actions.datasources.fetch()
|
datasources.fetch()
|
||||||
backendUiStore.actions.queries.fetch()
|
backendUiStore.actions.queries.fetch()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $backendUiStore.selectedDatabase && $backendUiStore.selectedDatabase._id}
|
{#if $backendUiStore.selectedDatabase && $backendUiStore.selectedDatabase._id}
|
||||||
<div class="hierarchy-items-container">
|
<div class="hierarchy-items-container">
|
||||||
{#each $backendUiStore.datasources as datasource, idx}
|
{#each $datasources.sources as datasource, idx}
|
||||||
<NavItem
|
<NavItem
|
||||||
border={idx > 0}
|
border={idx > 0}
|
||||||
text={datasource.name}
|
text={datasource.name}
|
||||||
selected={$backendUiStore.selectedDatasourceId === datasource._id}
|
selected={$datasources.selected === datasource._id}
|
||||||
on:click={() => selectDatasource(datasource)}>
|
on:click={() => selectDatasource(datasource)}>
|
||||||
<div class="datasource-icon" slot="icon">
|
<div class="datasource-icon" slot="icon">
|
||||||
<svelte:component
|
<svelte:component
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<script>
|
<script>
|
||||||
import { goto, params } from "@sveltech/routify"
|
import { goto, params } from "@sveltech/routify"
|
||||||
import { backendUiStore, store } from "builderStore"
|
import { datasources } from 'builderStore/store/backend/'
|
||||||
import { notifier } from "builderStore/store/notifications"
|
import { notifier } from "builderStore/store/notifications"
|
||||||
import { Input, Label, ModalContent, Button, Spacer } from "@budibase/bbui"
|
import { Input, Label, ModalContent } from "@budibase/bbui"
|
||||||
import TableIntegrationMenu from "../TableIntegrationMenu/index.svelte"
|
import TableIntegrationMenu from "../TableIntegrationMenu/index.svelte"
|
||||||
import analytics from "analytics"
|
import analytics from "analytics"
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
function checkValid(evt) {
|
function checkValid(evt) {
|
||||||
const datasourceName = evt.target.value
|
const datasourceName = evt.target.value
|
||||||
if (
|
if (
|
||||||
$backendUiStore.datasources?.some(
|
$datasources?.sources.some(
|
||||||
datasource => datasource.name === datasourceName
|
datasource => datasource.name === datasourceName
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
const { type, ...config } = integration
|
const { type, ...config } = integration
|
||||||
|
|
||||||
// Create datasource
|
// Create datasource
|
||||||
const response = await backendUiStore.actions.datasources.save({
|
const response = await datasources.save({
|
||||||
name,
|
name,
|
||||||
source: type,
|
source: type,
|
||||||
config,
|
config,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { goto } from "@sveltech/routify"
|
import { goto } from "@sveltech/routify"
|
||||||
import { backendUiStore } from "builderStore"
|
import { datasources } from 'builderStore/store/backend/'
|
||||||
import { notifier } from "builderStore/store/notifications"
|
import { notifier } from "builderStore/store/notifications"
|
||||||
import { DropdownMenu } from "@budibase/bbui"
|
import { DropdownMenu } from "@budibase/bbui"
|
||||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteDatasource() {
|
async function deleteDatasource() {
|
||||||
await backendUiStore.actions.datasources.delete(datasource)
|
await datasources.delete(datasource)
|
||||||
notifier.success("Datasource deleted")
|
notifier.success("Datasource deleted")
|
||||||
$goto('./datasource')
|
$goto('./datasource')
|
||||||
hideEditor()
|
hideEditor()
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
import { store, backendUiStore, currentAsset } from "builderStore"
|
import { store, backendUiStore, currentAsset } from "builderStore"
|
||||||
|
import { datasources } from 'builderStore/store/backend/'
|
||||||
import { notifier } from "builderStore/store/notifications"
|
import { notifier } from "builderStore/store/notifications"
|
||||||
import ParameterBuilder from "components/integration/QueryParameterBuilder.svelte"
|
import ParameterBuilder from "components/integration/QueryParameterBuilder.svelte"
|
||||||
import IntegrationQueryEditor from "components/integration/index.svelte"
|
import IntegrationQueryEditor from "components/integration/index.svelte"
|
||||||
|
@ -81,7 +82,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchQueryDefinition(query) {
|
function fetchQueryDefinition(query) {
|
||||||
const source = $backendUiStore.datasources.find(
|
const source = $datasources.sources.find(
|
||||||
ds => ds._id === query.datasourceId
|
ds => ds._id === query.datasourceId
|
||||||
).source
|
).source
|
||||||
return $backendUiStore.integrations[source].query[query.queryVerb]
|
return $backendUiStore.integrations[source].query[query.queryVerb]
|
||||||
|
@ -123,7 +124,7 @@
|
||||||
height={200}
|
height={200}
|
||||||
query={value}
|
query={value}
|
||||||
schema={fetchQueryDefinition(value)}
|
schema={fetchQueryDefinition(value)}
|
||||||
datasource={$backendUiStore.datasources.find(ds => ds._id === value.datasourceId)}
|
datasource={$datasources.sources.find(ds => ds._id === value.datasourceId)}
|
||||||
editable={false} />
|
editable={false} />
|
||||||
<Spacer large />
|
<Spacer large />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { Select, Label, Spacer } from "@budibase/bbui"
|
import { Select, Label, Spacer } from "@budibase/bbui"
|
||||||
import { store, backendUiStore, currentAsset } from "builderStore"
|
import { store, backendUiStore, currentAsset } from "builderStore"
|
||||||
|
import { datasources } from 'builderStore/store/backend/'
|
||||||
import { getBindableProperties } from "builderStore/dataBinding"
|
import { getBindableProperties } from "builderStore/dataBinding"
|
||||||
import ParameterBuilder from "components/integration/QueryParameterBuilder.svelte"
|
import ParameterBuilder from "components/integration/QueryParameterBuilder.svelte"
|
||||||
import IntegrationQueryEditor from "components/integration/index.svelte"
|
import IntegrationQueryEditor from "components/integration/index.svelte"
|
||||||
|
@ -8,7 +9,7 @@
|
||||||
export let parameters
|
export let parameters
|
||||||
|
|
||||||
$: query = $backendUiStore.queries.find(q => q._id === parameters.queryId)
|
$: query = $backendUiStore.queries.find(q => q._id === parameters.queryId)
|
||||||
$: datasource = $backendUiStore.datasources.find(
|
$: datasource = $datasources.sources.find(
|
||||||
ds => ds._id === parameters.datasourceId
|
ds => ds._id === parameters.datasourceId
|
||||||
)
|
)
|
||||||
$: bindableProperties = getBindableProperties(
|
$: bindableProperties = getBindableProperties(
|
||||||
|
@ -17,7 +18,7 @@
|
||||||
)
|
)
|
||||||
|
|
||||||
function fetchQueryDefinition(query) {
|
function fetchQueryDefinition(query) {
|
||||||
const source = $backendUiStore.datasources.find(
|
const source = $datasources.sources.find(
|
||||||
ds => ds._id === query.datasourceId
|
ds => ds._id === query.datasourceId
|
||||||
).source
|
).source
|
||||||
return $backendUiStore.integrations[source].query[query.queryVerb]
|
return $backendUiStore.integrations[source].query[query.queryVerb]
|
||||||
|
@ -27,7 +28,7 @@
|
||||||
<Label small>Datasource</Label>
|
<Label small>Datasource</Label>
|
||||||
<Select thin secondary bind:value={parameters.datasourceId}>
|
<Select thin secondary bind:value={parameters.datasourceId}>
|
||||||
<option value="" />
|
<option value="" />
|
||||||
{#each $backendUiStore.datasources as datasource}
|
{#each $datasources as datasource}
|
||||||
<option value={datasource._id}>{datasource.name}</option>
|
<option value={datasource._id}>{datasource.name}</option>
|
||||||
{/each}
|
{/each}
|
||||||
</Select>
|
</Select>
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
import ExternalDataSourceTable from "components/backend/DataTable/ExternalDataSourceTable.svelte"
|
import ExternalDataSourceTable from "components/backend/DataTable/ExternalDataSourceTable.svelte"
|
||||||
import ParameterBuilder from "components/integration/QueryParameterBuilder.svelte"
|
import ParameterBuilder from "components/integration/QueryParameterBuilder.svelte"
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
|
import { datasources } from 'builderStore/store/backend/'
|
||||||
|
|
||||||
const PREVIEW_HEADINGS = [
|
const PREVIEW_HEADINGS = [
|
||||||
{
|
{
|
||||||
|
@ -35,13 +36,11 @@
|
||||||
export let query
|
export let query
|
||||||
export let fields = []
|
export let fields = []
|
||||||
|
|
||||||
let config
|
|
||||||
let tab = "JSON"
|
let tab = "JSON"
|
||||||
let parameters
|
let parameters
|
||||||
let data = []
|
let data = []
|
||||||
let popover
|
|
||||||
|
|
||||||
$: datasource = $backendUiStore.datasources.find(
|
$: datasource = $datasources.sources.find(
|
||||||
ds => ds._id === query.datasourceId
|
ds => ds._id === query.datasourceId
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
<script>
|
<script>
|
||||||
import { params } from "@sveltech/routify"
|
import { params } from "@sveltech/routify"
|
||||||
import { backendUiStore } from "builderStore"
|
import { datasources } from 'builderStore/store/backend/'
|
||||||
|
|
||||||
if ($params.selectedDatasource) {
|
if ($params.selectedDatasource) {
|
||||||
const datasource = $backendUiStore.datasources.find(
|
const datasource = $datasources.sources.find(
|
||||||
m => m._id === $params.selectedDatasource
|
m => m._id === $params.selectedDatasource
|
||||||
)
|
)
|
||||||
if (datasource) {
|
if (datasource) {
|
||||||
backendUiStore.actions.datasources.select(datasource._id)
|
datasources.select(datasource._id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -2,20 +2,21 @@
|
||||||
import { goto, beforeUrlChange } from "@sveltech/routify"
|
import { goto, beforeUrlChange } from "@sveltech/routify"
|
||||||
import { Button, Heading, Body, Spacer } from "@budibase/bbui"
|
import { Button, Heading, Body, Spacer } from "@budibase/bbui"
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
|
import { datasources } from 'builderStore/store/backend/'
|
||||||
import { notifier } from "builderStore/store/notifications"
|
import { notifier } from "builderStore/store/notifications"
|
||||||
import IntegrationConfigForm from "components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte"
|
import IntegrationConfigForm from "components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte"
|
||||||
import ICONS from "components/backend/DatasourceNavigator/icons"
|
import ICONS from "components/backend/DatasourceNavigator/icons"
|
||||||
|
|
||||||
let unsaved = false
|
let unsaved = false
|
||||||
|
|
||||||
$: datasource = $backendUiStore.datasources.find(
|
$: datasource = $datasources.sources.find(
|
||||||
ds => ds._id === $backendUiStore.selectedDatasourceId
|
ds => ds._id === $datasources.selected
|
||||||
)
|
)
|
||||||
$: integration = datasource && $backendUiStore.integrations[datasource.source]
|
$: integration = datasource && $backendUiStore.integrations[datasource.source]
|
||||||
|
|
||||||
async function saveDatasource() {
|
async function saveDatasource() {
|
||||||
// Create datasource
|
// Create datasource
|
||||||
await backendUiStore.actions.datasources.save(datasource)
|
await datasources.save(datasource)
|
||||||
notifier.success(`Datasource ${name} saved successfully.`)
|
notifier.success(`Datasource ${name} saved successfully.`)
|
||||||
unsaved = false
|
unsaved = false
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { backendUiStore } from "builderStore"
|
import { datasources } from 'builderStore/store/backend/'
|
||||||
import { goto, leftover } from "@sveltech/routify"
|
import { goto, leftover } from "@sveltech/routify"
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
|
|
||||||
|
@ -7,10 +7,10 @@
|
||||||
// navigate to first datasource in list, if not already selected
|
// navigate to first datasource in list, if not already selected
|
||||||
if (
|
if (
|
||||||
!$leftover &&
|
!$leftover &&
|
||||||
$backendUiStore.datasources.length > 0 &&
|
$datasources.sources.length > 0 &&
|
||||||
!$backendUiStore.selectedDatasourceId
|
!$datasources.selected
|
||||||
) {
|
) {
|
||||||
$goto(`./${$backendUiStore.datasources[0]._id}`)
|
$goto(`./${$datasources.sources[0]._id}`)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
<script>
|
<script>
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
|
import { datasources } from 'builderStore/store/backend/'
|
||||||
import { goto } from "@sveltech/routify"
|
import { goto } from "@sveltech/routify"
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
// navigate to first table in list, if not already selected
|
// navigate to first table in list, if not already selected
|
||||||
$backendUiStore.datasources.length > 0 && $goto(`../${$backendUiStore.datasources[0]._id}`)
|
$datasources.sources.length > 0 && $goto(`../${$datasources.sources[0]._id}`)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue