mirror of
https://gitlab.com/bramw/baserow.git
synced 2025-04-14 09:08:32 +00:00
"From template" onboarding step
This commit is contained in:
parent
4d4e67c2a7
commit
c44b3295ac
20 changed files with 292 additions and 34 deletions
backend/templates
content-scheduling-manager.jsonemployee-directory.jsonlightweight-crm.jsonnew-hire-onboarding.jsonobjectives-key-results.jsonuser-feedback.json
changelog/entries/unreleased/feature
web-frontend/modules
core
database
|
@ -12,7 +12,8 @@
|
||||||
"publisher",
|
"publisher",
|
||||||
"planner",
|
"planner",
|
||||||
"publish",
|
"publish",
|
||||||
"social media"
|
"social media",
|
||||||
|
"onboarding"
|
||||||
],
|
],
|
||||||
"categories": [
|
"categories": [
|
||||||
"Marketing"
|
"Marketing"
|
||||||
|
@ -4891,4 +4892,4 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,8 @@
|
||||||
"teams",
|
"teams",
|
||||||
"employee list",
|
"employee list",
|
||||||
"staff catalog",
|
"staff catalog",
|
||||||
"staff records"
|
"staff records",
|
||||||
|
"onboarding"
|
||||||
],
|
],
|
||||||
"categories": [
|
"categories": [
|
||||||
"HR and Recruiting"
|
"HR and Recruiting"
|
||||||
|
@ -3777,4 +3778,4 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"CRM",
|
"CRM",
|
||||||
"Sales",
|
"Sales",
|
||||||
"Local Business"
|
"Local Business",
|
||||||
|
"onboarding"
|
||||||
],
|
],
|
||||||
"categories": [
|
"categories": [
|
||||||
"Sales and CRM"
|
"Sales and CRM"
|
||||||
|
@ -25570,4 +25571,4 @@
|
||||||
"type": "builder"
|
"type": "builder"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,8 @@
|
||||||
"recruits",
|
"recruits",
|
||||||
"hiring",
|
"hiring",
|
||||||
"hr",
|
"hr",
|
||||||
"human resources"
|
"human resources",
|
||||||
|
"onboarding"
|
||||||
],
|
],
|
||||||
"categories": [
|
"categories": [
|
||||||
"HR and Recruiting"
|
"HR and Recruiting"
|
||||||
|
@ -11144,4 +11145,4 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,8 @@
|
||||||
"goal tracker",
|
"goal tracker",
|
||||||
"business goal tracker",
|
"business goal tracker",
|
||||||
"objectives tracker",
|
"objectives tracker",
|
||||||
"progress tracker"
|
"progress tracker",
|
||||||
|
"onboarding"
|
||||||
],
|
],
|
||||||
"categories": [
|
"categories": [
|
||||||
"Business Strategy"
|
"Business Strategy"
|
||||||
|
@ -6312,4 +6313,4 @@
|
||||||
"type": "builder"
|
"type": "builder"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,8 @@
|
||||||
"customer survey",
|
"customer survey",
|
||||||
"client feedback",
|
"client feedback",
|
||||||
"UAT",
|
"UAT",
|
||||||
"user acceptance testing"
|
"user acceptance testing",
|
||||||
|
"onboarding"
|
||||||
],
|
],
|
||||||
"categories": [
|
"categories": [
|
||||||
"Product Management",
|
"Product Management",
|
||||||
|
@ -3716,4 +3717,4 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"type": "feature",
|
||||||
|
"message": "Install template during onboarding",
|
||||||
|
"domain": "database",
|
||||||
|
"issue_number": 2638,
|
||||||
|
"bullet_points": [],
|
||||||
|
"created_at": "2025-03-24"
|
||||||
|
}
|
|
@ -184,3 +184,4 @@
|
||||||
@import 'admin_dashboard';
|
@import 'admin_dashboard';
|
||||||
@import 'user_admin';
|
@import 'user_admin';
|
||||||
@import 'group_admin';
|
@import 'group_admin';
|
||||||
|
@import 'template_import_item';
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
transition: left 0.5s ease;
|
transition: left 0.5s ease;
|
||||||
|
z-index: $z-index-layout-col-1;
|
||||||
|
|
||||||
@include absolute(10%, 0, 0, 10%);
|
@include absolute(10%, 0, 0, 10%);
|
||||||
}
|
}
|
||||||
|
@ -15,6 +16,10 @@
|
||||||
|
|
||||||
.onboarding-tool-preview__inner {
|
.onboarding-tool-preview__inner {
|
||||||
@include absolute(0, 0, 0, 0);
|
@include absolute(0, 0, 0, 0);
|
||||||
|
|
||||||
|
&--reserve-space-right {
|
||||||
|
right: -370px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.onboarding-tool-preview__highlight {
|
.onboarding-tool-preview__highlight {
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
.template-import-items {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.template-import-item {
|
||||||
|
&:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.template-import-item__head {
|
||||||
|
border: 1px solid $palette-neutral-200;
|
||||||
|
background: $palette-neutral-25;
|
||||||
|
height: 76px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
|
||||||
|
@include rounded($rounded-md);
|
||||||
|
|
||||||
|
.template-import-item--active & {
|
||||||
|
border: 2px solid $palette-blue-500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.template-import-item__icon {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex: 0 0 40px;
|
||||||
|
height: 40px;
|
||||||
|
font-size: 16px;
|
||||||
|
border: solid 1px $palette-neutral-200;
|
||||||
|
border-radius: 100%;
|
||||||
|
color: $palette-neutral-700;
|
||||||
|
background-color: $white;
|
||||||
|
|
||||||
|
@include rounded($rounded-md);
|
||||||
|
@include elevation($elevation-low);
|
||||||
|
}
|
||||||
|
|
||||||
|
.template-import-item__name {
|
||||||
|
@extend %ellipsis;
|
||||||
|
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 20px;
|
||||||
|
color: $palette-neutral-1200;
|
||||||
|
}
|
|
@ -218,6 +218,10 @@
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.justify-content-center {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
.justify-content-end {
|
.justify-content-end {
|
||||||
justify-content: end;
|
justify-content: end;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,6 @@ $base-font-family: $text-font-stack !default;
|
||||||
|
|
||||||
$radius-none: 0 !default;
|
$radius-none: 0 !default;
|
||||||
$rounded: 4px !default;
|
$rounded: 4px !default;
|
||||||
$rounded-left: 4px 0 0 4px !default;
|
|
||||||
$rounded-md: 6px !default;
|
$rounded-md: 6px !default;
|
||||||
$rounded-xl: 12px !default;
|
$rounded-xl: 12px !default;
|
||||||
$rounded-2xl: 16px !default;
|
$rounded-2xl: 16px !default;
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
:class="{
|
:class="{
|
||||||
'segment-control__button--active': index === activeIndex,
|
'segment-control__button--active': index === activeIndex,
|
||||||
}"
|
}"
|
||||||
|
:title="segment.label"
|
||||||
class="segment-control__button"
|
class="segment-control__button"
|
||||||
@click="setActiveIndex(index)"
|
@click="setActiveIndex(index)"
|
||||||
>
|
>
|
||||||
|
|
|
@ -35,7 +35,7 @@ export class OnboardingType extends Registerable {
|
||||||
* so it should just be for demo purposes. It can accept the data property
|
* so it should just be for demo purposes. It can accept the data property
|
||||||
* containing the data of all the steps.
|
* containing the data of all the steps.
|
||||||
*/
|
*/
|
||||||
getPreviewComponent() {
|
getPreviewComponent(data) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,7 +83,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="onboarding__preview">
|
<div class="onboarding__preview">
|
||||||
<component
|
<component
|
||||||
:is="step.getPreviewComponent()"
|
:is="step.getPreviewComponent(data)"
|
||||||
v-bind="step.getAdditionalPreviewProps()"
|
v-bind="step.getAdditionalPreviewProps()"
|
||||||
:data="data"
|
:data="data"
|
||||||
></component>
|
></component>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<p>
|
<p>
|
||||||
{{ $t('databaseStep.description') }}
|
{{ $t('databaseStep.description') }}
|
||||||
</p>
|
</p>
|
||||||
<div class="margin-bottom-3">
|
<div class="margin-bottom-2">
|
||||||
<SegmentControl
|
<SegmentControl
|
||||||
:active-index.sync="selectedTypeIndex"
|
:active-index.sync="selectedTypeIndex"
|
||||||
:segments="types"
|
:segments="types"
|
||||||
|
@ -31,17 +31,29 @@
|
||||||
ref="airtable"
|
ref="airtable"
|
||||||
@input="updateValue($event)"
|
@input="updateValue($event)"
|
||||||
></AirtableImportForm>
|
></AirtableImportForm>
|
||||||
|
<TemplateImportForm
|
||||||
|
v-if="selectedType === 'template'"
|
||||||
|
@selected-template="selectedTemplate"
|
||||||
|
></TemplateImportForm>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { useVuelidate } from '@vuelidate/core'
|
import { useVuelidate } from '@vuelidate/core'
|
||||||
import { required, helpers } from '@vuelidate/validators'
|
import { required, helpers } from '@vuelidate/validators'
|
||||||
import AirtableImportForm from '@baserow/modules/database/components/airtable/AirtableImportForm.vue'
|
import AirtableImportForm from '@baserow/modules/database/components/airtable/AirtableImportForm'
|
||||||
|
import TemplateImportForm from '@baserow/modules/database/components/onboarding/TemplateImportForm'
|
||||||
|
import { DatabaseOnboardingType } from '@baserow/modules/database/onboardingTypes'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DatabaseStep',
|
name: 'DatabaseStep',
|
||||||
components: { AirtableImportForm },
|
components: { AirtableImportForm, TemplateImportForm },
|
||||||
|
props: {
|
||||||
|
data: {
|
||||||
|
required: true,
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
},
|
||||||
setup() {
|
setup() {
|
||||||
return { v$: useVuelidate({ $lazy: true }) }
|
return { v$: useVuelidate({ $lazy: true }) }
|
||||||
},
|
},
|
||||||
|
@ -60,6 +72,10 @@ export default {
|
||||||
type: 'airtable',
|
type: 'airtable',
|
||||||
label: this.$t('databaseStep.airtable'),
|
label: this.$t('databaseStep.airtable'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'template',
|
||||||
|
label: this.$t('databaseStep.template'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
selectedTypeIndex: 0,
|
selectedTypeIndex: 0,
|
||||||
name: '',
|
name: '',
|
||||||
|
@ -82,6 +98,9 @@ export default {
|
||||||
if (this.selectedType === 'airtable') {
|
if (this.selectedType === 'airtable') {
|
||||||
const airtable = this.$refs.airtable
|
const airtable = this.$refs.airtable
|
||||||
return !!airtable && !airtable.v$.$invalid && airtable.v$.$dirty
|
return !!airtable && !airtable.v$.$invalid && airtable.v$.$dirty
|
||||||
|
} else if (this.selectedType === 'template') {
|
||||||
|
const template = this.data[DatabaseOnboardingType.getType()].template
|
||||||
|
return !!template
|
||||||
} else {
|
} else {
|
||||||
return !this.v$.$invalid && this.v$.$dirty
|
return !this.v$.$invalid && this.v$.$dirty
|
||||||
}
|
}
|
||||||
|
@ -93,6 +112,12 @@ export default {
|
||||||
...airtable,
|
...airtable,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
selectedTemplate(template) {
|
||||||
|
this.$emit('update-data', {
|
||||||
|
type: this.selectedType,
|
||||||
|
template,
|
||||||
|
})
|
||||||
|
},
|
||||||
},
|
},
|
||||||
validations() {
|
validations() {
|
||||||
const rules = {}
|
const rules = {}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
<template>
|
||||||
|
<div :class="{ 'onboarding-tool-preview': true }">
|
||||||
|
<div
|
||||||
|
ref="inner"
|
||||||
|
class="onboarding-tool-preview__inner onboarding-tool-preview__inner--reserve-space-right"
|
||||||
|
>
|
||||||
|
<TemplatePreview :template="template"></TemplatePreview>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import TemplatePreview from '@baserow/modules/core/components/template/TemplatePreview'
|
||||||
|
import { DatabaseOnboardingType } from '@baserow/modules/database/onboardingTypes'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DatabaseTemplatePreview',
|
||||||
|
components: { TemplatePreview },
|
||||||
|
props: {
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
template() {
|
||||||
|
return this.data[DatabaseOnboardingType.getType()].template
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,97 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div v-if="loading" class="flex justify-content-center">
|
||||||
|
<div class="loading"></div>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<FormInput
|
||||||
|
v-model="search"
|
||||||
|
icon-left="iconoir-search"
|
||||||
|
:placeholder="$t('templateCategories.search')"
|
||||||
|
class="margin-bottom-2"
|
||||||
|
></FormInput>
|
||||||
|
<div class="template-import-items">
|
||||||
|
<a
|
||||||
|
v-for="template in templates"
|
||||||
|
:key="template.id"
|
||||||
|
class="template-import-item"
|
||||||
|
:class="{
|
||||||
|
'template-import-item--active': template.id === selectedTemplate,
|
||||||
|
}"
|
||||||
|
@click="selectTemplate(template)"
|
||||||
|
>
|
||||||
|
<div class="template-import-item__head">
|
||||||
|
<i class="template-import-item__icon" :class="template.icon"></i>
|
||||||
|
</div>
|
||||||
|
<div class="template-import-item__name">{{ template.name }}</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import TemplateService from '@baserow/modules/core/services/template'
|
||||||
|
import { notifyIf } from '@baserow/modules/core/utils/error'
|
||||||
|
import { escapeRegExp } from '@baserow/modules/core/utils/string'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TemplateImportForm',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
categories: [],
|
||||||
|
search: '',
|
||||||
|
selectedTemplate: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
templates() {
|
||||||
|
let allTemplates = []
|
||||||
|
this.categories.forEach((category) => {
|
||||||
|
category.templates.forEach((template) => {
|
||||||
|
// Categories can have the same templates. We should not have duplicates.
|
||||||
|
if (allTemplates.findIndex((t) => t.id === template.id) === -1) {
|
||||||
|
allTemplates.push(template)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// A few selected templates have the keyword `onboarding`. These are shown when
|
||||||
|
// no search query is provided.
|
||||||
|
const search = this.search || 'onboarding'
|
||||||
|
allTemplates = allTemplates
|
||||||
|
.filter((template) => {
|
||||||
|
// If `open_application == null`, then it falls back on the normal behavior,
|
||||||
|
// which is opening the first database. An `open_application` is typically
|
||||||
|
// set, if an application must be opened first. The onboarding experience
|
||||||
|
// works best by starting with a database, so we're filtering those out.
|
||||||
|
return template.open_application === null
|
||||||
|
})
|
||||||
|
.filter((template) => {
|
||||||
|
const keywords = template.keywords.split(',')
|
||||||
|
keywords.push(template.name)
|
||||||
|
const regex = new RegExp('(' + escapeRegExp(search) + ')', 'i')
|
||||||
|
return keywords.some((value) => value.match(regex))
|
||||||
|
})
|
||||||
|
|
||||||
|
return allTemplates.slice(0, 6)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
|
try {
|
||||||
|
const { data } = await TemplateService(this.$client).fetchAll()
|
||||||
|
this.categories = data
|
||||||
|
} catch (error) {
|
||||||
|
notifyIf(error, 'templates')
|
||||||
|
}
|
||||||
|
this.loading = false
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
selectTemplate(template) {
|
||||||
|
this.selectedTemplate = template.id
|
||||||
|
this.$emit('selected-template', template)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -1001,11 +1001,12 @@
|
||||||
},
|
},
|
||||||
"databaseStep": {
|
"databaseStep": {
|
||||||
"title": "Create your first database",
|
"title": "Create your first database",
|
||||||
"description": "Let us know what you're working on.",
|
"description": "Select what where you'd like to start from:",
|
||||||
"databaseNameLabel": "Database name",
|
"databaseNameLabel": "Database name",
|
||||||
"scratch": "From scratch",
|
"scratch": "Scratch",
|
||||||
"import": "From file",
|
"import": "File",
|
||||||
"airtable": "From Airtable"
|
"airtable": "Airtable",
|
||||||
|
"template": "Template"
|
||||||
},
|
},
|
||||||
"ViewFilterTypeDateUpgradeToMultiStep": {
|
"ViewFilterTypeDateUpgradeToMultiStep": {
|
||||||
"migrateButtonText": "Migrate to multi-step date filter",
|
"migrateButtonText": "Migrate to multi-step date filter",
|
||||||
|
|
|
@ -14,6 +14,8 @@ import FieldService from '@baserow/modules/database/services/field'
|
||||||
import RowService from '@baserow/modules/database/services/row'
|
import RowService from '@baserow/modules/database/services/row'
|
||||||
import AirtableService from '@baserow/modules/database/services/airtable'
|
import AirtableService from '@baserow/modules/database/services/airtable'
|
||||||
import DatabaseScratchTrackFieldsStep from '@baserow/modules/database/components/onboarding/DatabaseScratchTrackFieldsStep.vue'
|
import DatabaseScratchTrackFieldsStep from '@baserow/modules/database/components/onboarding/DatabaseScratchTrackFieldsStep.vue'
|
||||||
|
import DatabaseTemplatePreview from '@baserow/modules/database/components/onboarding/DatabaseTemplatePreview'
|
||||||
|
import TemplateService from '@baserow/modules/core/services/template'
|
||||||
|
|
||||||
const databaseTypeCondition = (data, type) => {
|
const databaseTypeCondition = (data, type) => {
|
||||||
const dependingType = DatabaseOnboardingType.getType()
|
const dependingType = DatabaseOnboardingType.getType()
|
||||||
|
@ -52,8 +54,14 @@ export class DatabaseOnboardingType extends OnboardingType {
|
||||||
return DatabaseStep
|
return DatabaseStep
|
||||||
}
|
}
|
||||||
|
|
||||||
getPreviewComponent() {
|
getPreviewComponent(data) {
|
||||||
return DatabaseAppLayoutPreview
|
const type = data[this.getType()]?.type
|
||||||
|
const template = data[this.getType()]?.template
|
||||||
|
if (type === 'template' && template) {
|
||||||
|
return DatabaseTemplatePreview
|
||||||
|
} else {
|
||||||
|
return DatabaseAppLayoutPreview
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getAdditionalPreviewProps() {
|
getAdditionalPreviewProps() {
|
||||||
|
@ -61,14 +69,15 @@ export class DatabaseOnboardingType extends OnboardingType {
|
||||||
}
|
}
|
||||||
|
|
||||||
async complete(data, responses) {
|
async complete(data, responses) {
|
||||||
const type = data[this.getType()].type
|
const workspace = responses[WorkspaceOnboardingType.getType()]
|
||||||
if (type === 'airtable') {
|
const stepData = data[this.getType()]
|
||||||
const workspace = responses[WorkspaceOnboardingType.getType()]
|
const fromType = stepData.type
|
||||||
const airtableUrl = data[this.getType()].airtableUrl
|
if (fromType === 'airtable') {
|
||||||
const skipFiles = data[this.getType()].skipFiles
|
const airtableUrl = stepData.airtableUrl
|
||||||
const useSession = data[this.getType()].useSession
|
const skipFiles = stepData.skipFiles
|
||||||
const session = data[this.getType()].session
|
const useSession = stepData.useSession
|
||||||
const sessionSignature = data[this.getType()].sessionSignature
|
const session = stepData.session
|
||||||
|
const sessionSignature = stepData.sessionSignature
|
||||||
const { data: job } = await AirtableService(this.app.$client).create(
|
const { data: job } = await AirtableService(this.app.$client).create(
|
||||||
workspace.id,
|
workspace.id,
|
||||||
airtableUrl,
|
airtableUrl,
|
||||||
|
@ -77,6 +86,15 @@ export class DatabaseOnboardingType extends OnboardingType {
|
||||||
useSession ? sessionSignature : null
|
useSession ? sessionSignature : null
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Responds with the newly created job, so that the `getJobForPolling` can use
|
||||||
|
// the response to mark the onboarding as an async job.
|
||||||
|
return job
|
||||||
|
} else if (fromType === 'template') {
|
||||||
|
const template = stepData.template
|
||||||
|
const { data: job } = await TemplateService(
|
||||||
|
this.app.$client
|
||||||
|
).asyncInstall(workspace.id, template.id)
|
||||||
|
|
||||||
// Responds with the newly created job, so that the `getJobForPolling` can use
|
// Responds with the newly created job, so that the `getJobForPolling` can use
|
||||||
// the response to mark the onboarding as an async job.
|
// the response to mark the onboarding as an async job.
|
||||||
return job
|
return job
|
||||||
|
@ -85,15 +103,26 @@ export class DatabaseOnboardingType extends OnboardingType {
|
||||||
|
|
||||||
getJobForPolling(data, responses) {
|
getJobForPolling(data, responses) {
|
||||||
const type = data[this.getType()].type
|
const type = data[this.getType()].type
|
||||||
if (type === 'airtable') {
|
if (type === 'airtable' || type === 'template') {
|
||||||
return responses[this.getType()]
|
return responses[this.getType()]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getCompletedRoute(data, responses) {
|
getCompletedRoute(data, responses) {
|
||||||
const type = data[this.getType()].type
|
const type = data[this.getType()].type
|
||||||
|
let database = null
|
||||||
if (type === 'airtable') {
|
if (type === 'airtable') {
|
||||||
const database = responses[this.getType()].database
|
database = responses[this.getType()].database
|
||||||
|
} else if (type === 'template') {
|
||||||
|
database = responses[this.getType()].installed_applications.find(
|
||||||
|
(application) => application.type === DatabaseApplicationType.getType()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deliberately open the database first because that's where the user must start
|
||||||
|
// their journey. If no database exist, return nothing, so that the dashboard
|
||||||
|
// is opened.
|
||||||
|
if (database) {
|
||||||
const firstTableId = database.tables[0]?.id || 0
|
const firstTableId = database.tables[0]?.id || 0
|
||||||
return {
|
return {
|
||||||
name: 'database-table',
|
name: 'database-table',
|
||||||
|
|
Loading…
Add table
Reference in a new issue