mirror of
https://github.com/nocodb/nocodb.git
synced 2026-04-25 10:25:20 +00:00
chore(nc-gui): lint
This commit is contained in:
@@ -3,8 +3,8 @@ import { FormBuilderValidatorType } from 'nocodb-sdk'
|
||||
import { FORM_BUILDER_NON_CATEGORIZED, FormBuilderInputType } from '#imports'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
baseId?: string
|
||||
visible: boolean
|
||||
baseId?: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(['update:visible'])
|
||||
@@ -16,160 +16,158 @@ const { $api } = useNuxtApp()
|
||||
const { t } = useI18n()
|
||||
|
||||
const initialSanboxFormState = ref<Record<string, any>>({
|
||||
title: '',
|
||||
description: '',
|
||||
category: '',
|
||||
visibility: 'private',
|
||||
title: '',
|
||||
description: '',
|
||||
category: '',
|
||||
visibility: 'private',
|
||||
})
|
||||
|
||||
const workspaceStore = useWorkspace()
|
||||
|
||||
|
||||
const { activeWorkspaceId } = storeToRefs(workspaceStore)
|
||||
const { base } = storeToRefs(useBase())
|
||||
|
||||
const basesStore = useBases()
|
||||
|
||||
const convertToSandbox = async (formState: Record<string, any>) => {
|
||||
try {
|
||||
const response = await $api.internal.postOperation(
|
||||
activeWorkspaceId.value as string,
|
||||
props.baseId,
|
||||
{
|
||||
operation: 'sandboxCreate',
|
||||
} as any,
|
||||
{
|
||||
title: formState.title,
|
||||
description: formState.description,
|
||||
category: formState.category,
|
||||
visibility: formState.visibility,
|
||||
},
|
||||
)
|
||||
try {
|
||||
const response = await $api.internal.postOperation(
|
||||
activeWorkspaceId.value as string,
|
||||
props.baseId,
|
||||
{
|
||||
operation: 'sandboxCreate',
|
||||
} as any,
|
||||
{
|
||||
title: formState.title,
|
||||
description: formState.description,
|
||||
category: formState.category,
|
||||
visibility: formState.visibility,
|
||||
},
|
||||
)
|
||||
|
||||
message.success(t('msg.success.sandboxCreated'))
|
||||
visible.value = false
|
||||
message.success(t('msg.success.sandboxCreated'))
|
||||
visible.value = false
|
||||
|
||||
// Update the base with the sandbox_id from response
|
||||
if (response && response.sandbox_id) {
|
||||
const currentBase = basesStore.bases.get(props.baseId)
|
||||
if (currentBase) {
|
||||
; (currentBase as any).sandbox_id = response.sandbox_id
|
||||
}
|
||||
}
|
||||
|
||||
// Reload base to ensure all sandbox data is loaded
|
||||
await basesStore.loadProject(props.baseId, true)
|
||||
} catch (e: any) {
|
||||
message.error(await extractSdkResponseErrorMsg(e))
|
||||
// Update the base with the sandbox_id from response
|
||||
if (response && response.sandbox_id) {
|
||||
const currentBase = basesStore.bases.get(props.baseId)
|
||||
if (currentBase) {
|
||||
;(currentBase as any).sandbox_id = response.sandbox_id
|
||||
}
|
||||
}
|
||||
|
||||
// Reload base to ensure all sandbox data is loaded
|
||||
await basesStore.loadProject(props.baseId, true)
|
||||
} catch (e: any) {
|
||||
message.error(await extractSdkResponseErrorMsg(e))
|
||||
}
|
||||
}
|
||||
|
||||
const { formState, isLoading, submit } = useProvideFormBuilderHelper({
|
||||
formSchema: [
|
||||
formSchema: [
|
||||
{
|
||||
type: FormBuilderInputType.Input,
|
||||
label: t('labels.sandboxTitle'),
|
||||
span: 24,
|
||||
model: 'title',
|
||||
placeholder: 'Enter a descriptive title',
|
||||
category: FORM_BUILDER_NON_CATEGORIZED,
|
||||
validators: [
|
||||
{
|
||||
type: FormBuilderInputType.Input,
|
||||
label: t('labels.sandboxTitle'),
|
||||
span: 24,
|
||||
model: 'title',
|
||||
placeholder: 'Enter a descriptive title',
|
||||
category: FORM_BUILDER_NON_CATEGORIZED,
|
||||
validators: [
|
||||
{
|
||||
type: FormBuilderValidatorType.Required,
|
||||
message: t('labels.titleRequired'),
|
||||
},
|
||||
],
|
||||
required: true,
|
||||
type: FormBuilderValidatorType.Required,
|
||||
message: t('labels.titleRequired'),
|
||||
},
|
||||
{
|
||||
type: FormBuilderInputType.Textarea,
|
||||
label: t('labels.sandboxDescription'),
|
||||
span: 24,
|
||||
model: 'description',
|
||||
placeholder: "Describe your application's capabilities",
|
||||
category: FORM_BUILDER_NON_CATEGORIZED,
|
||||
},
|
||||
{
|
||||
type: FormBuilderInputType.Input,
|
||||
label: t('labels.sandboxCategory'),
|
||||
span: 12,
|
||||
model: 'category',
|
||||
placeholder: 'e.g., CRM, HR',
|
||||
category: FORM_BUILDER_NON_CATEGORIZED,
|
||||
},
|
||||
{
|
||||
type: FormBuilderInputType.Select,
|
||||
label: t('labels.sandboxVisibility'),
|
||||
span: 12,
|
||||
model: 'visibility',
|
||||
category: FORM_BUILDER_NON_CATEGORIZED,
|
||||
options: [
|
||||
{ label: 'Public', value: 'public', icon: 'eye' },
|
||||
{ label: 'Private', value: 'private', icon: 'lock' },
|
||||
{ label: 'Unlisted', value: 'unlisted', icon: 'ncEyeOff' },
|
||||
],
|
||||
defaultValue: 'private',
|
||||
},
|
||||
],
|
||||
onSubmit: async () => {
|
||||
return await convertToSandbox(formState.value)
|
||||
],
|
||||
required: true,
|
||||
},
|
||||
initialState: initialSanboxFormState,
|
||||
{
|
||||
type: FormBuilderInputType.Textarea,
|
||||
label: t('labels.sandboxDescription'),
|
||||
span: 24,
|
||||
model: 'description',
|
||||
placeholder: "Describe your application's capabilities",
|
||||
category: FORM_BUILDER_NON_CATEGORIZED,
|
||||
},
|
||||
{
|
||||
type: FormBuilderInputType.Input,
|
||||
label: t('labels.sandboxCategory'),
|
||||
span: 12,
|
||||
model: 'category',
|
||||
placeholder: 'e.g., CRM, HR',
|
||||
category: FORM_BUILDER_NON_CATEGORIZED,
|
||||
},
|
||||
{
|
||||
type: FormBuilderInputType.Select,
|
||||
label: t('labels.sandboxVisibility'),
|
||||
span: 12,
|
||||
model: 'visibility',
|
||||
category: FORM_BUILDER_NON_CATEGORIZED,
|
||||
options: [
|
||||
{ label: 'Public', value: 'public', icon: 'eye' },
|
||||
{ label: 'Private', value: 'private', icon: 'lock' },
|
||||
{ label: 'Unlisted', value: 'unlisted', icon: 'ncEyeOff' },
|
||||
],
|
||||
defaultValue: 'private',
|
||||
},
|
||||
],
|
||||
onSubmit: async () => {
|
||||
return await convertToSandbox(formState.value)
|
||||
},
|
||||
initialState: initialSanboxFormState,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col">
|
||||
<div class="p-4 w-full flex items-center gap-3 border-b border-nc-border-gray-medium">
|
||||
<div
|
||||
class="w-10 h-10 rounded-lg bg-gradient-to-br from-brand-500 to-brand-600 flex items-center justify-center">
|
||||
<GeneralIcon icon="ncBox" class="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="font-semibold text-lg text-nc-content-gray-emphasis">
|
||||
Create Managed App
|
||||
</div>
|
||||
<div class="text-xs text-nc-content-gray-subtle2">{{ $t('labels.publishToAppStore') }}</div>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<div class="p-4 w-full flex items-center gap-3 border-b border-nc-border-gray-medium">
|
||||
<div class="w-10 h-10 rounded-lg bg-gradient-to-br from-brand-500 to-brand-600 flex items-center justify-center">
|
||||
<GeneralIcon icon="ncBox" class="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="font-semibold text-lg text-nc-content-gray-emphasis">Create Managed App</div>
|
||||
<div class="text-xs text-nc-content-gray-subtle2">{{ $t('labels.publishToAppStore') }}</div>
|
||||
</div>
|
||||
|
||||
<NcButton size="small" type="text" @click="visible = false" class="self-start">
|
||||
<GeneralIcon icon="close" class="text-nc-content-gray-subtle2" />
|
||||
</NcButton>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 p-6 nc-scrollbar-thin">
|
||||
<NcFormBuilder>
|
||||
<template #header>
|
||||
<NcAlert type="info" :align="'top'"
|
||||
description="Create managed application that can be published to the App Store. You'll be able to manage versions and push updates to all installations."
|
||||
class="!p-3 !items-start bg-nc-bg-blue-light border-1 !border-nc-blue-200 rounded-lg p-3 mb-4">
|
||||
<template #icon>
|
||||
<GeneralIcon icon="info" class="w-4 h-4 mt-0.5 text-nc-content-blue-dark flex-none" />
|
||||
</template>
|
||||
</NcAlert>
|
||||
</template>
|
||||
</NcFormBuilder>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end gap-2 px-6 py-3 border-t border-nc-border-gray-medium">
|
||||
<NcButton size="small" type="secondary" :disabled="isLoading" @click="visible = false">
|
||||
{{ $t('general.cancel') }}
|
||||
</NcButton>
|
||||
<NcButton size="small" type="primary" :loading="isLoading" @click="submit">
|
||||
<template #icon>
|
||||
<GeneralIcon icon="ncBox" />
|
||||
</template>
|
||||
Convert to sandbox
|
||||
</NcButton>
|
||||
</div>
|
||||
<NcButton size="small" type="text" class="self-start" @click="visible = false">
|
||||
<GeneralIcon icon="close" class="text-nc-content-gray-subtle2" />
|
||||
</NcButton>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 p-6 nc-scrollbar-thin">
|
||||
<NcFormBuilder>
|
||||
<template #header>
|
||||
<NcAlert
|
||||
type="info"
|
||||
align="top"
|
||||
description="Create managed application that can be published to the App Store. You'll be able to manage versions and push updates to all installations."
|
||||
class="!p-3 !items-start bg-nc-bg-blue-light border-1 !border-nc-blue-200 rounded-lg p-3 mb-4"
|
||||
>
|
||||
<template #icon>
|
||||
<GeneralIcon icon="info" class="w-4 h-4 mt-0.5 text-nc-content-blue-dark flex-none" />
|
||||
</template>
|
||||
</NcAlert>
|
||||
</template>
|
||||
</NcFormBuilder>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end gap-2 px-6 py-3 border-t border-nc-border-gray-medium">
|
||||
<NcButton size="small" type="secondary" :disabled="isLoading" @click="visible = false">
|
||||
{{ $t('general.cancel') }}
|
||||
</NcButton>
|
||||
<NcButton size="small" type="primary" :loading="isLoading" @click="submit">
|
||||
<template #icon>
|
||||
<GeneralIcon icon="ncBox" />
|
||||
</template>
|
||||
Convert to sandbox
|
||||
</NcButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.nc-modal-convert-to-sandbox {
|
||||
.nc-modal {
|
||||
max-height: min(90vh, 540px) !important;
|
||||
height: min(90vh, 540px) !important;
|
||||
}
|
||||
.nc-modal {
|
||||
max-height: min(90vh, 540px) !important;
|
||||
height: min(90vh, 540px) !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
<script lang="ts" setup>
|
||||
interface Props {
|
||||
visible: boolean
|
||||
variant: 'modal' | 'dropdown'
|
||||
baseCreateMode: NcBaseCreateMode | null
|
||||
visible: boolean
|
||||
variant: 'modal' | 'dropdown'
|
||||
baseCreateMode: NcBaseCreateMode | null
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
variant: 'dropdown',
|
||||
variant: 'dropdown',
|
||||
})
|
||||
|
||||
const emits = defineEmits<{
|
||||
(e: 'update:visible', value: boolean): void
|
||||
(e: 'update:baseCreateMode', value: NcBaseCreateMode | null): void
|
||||
(e: 'onSelect', mode: NcBaseCreateMode): void
|
||||
(e: 'update:visible', value: boolean): void
|
||||
(e: 'update:baseCreateMode', value: NcBaseCreateMode | null): void
|
||||
(e: 'onSelect', mode: NcBaseCreateMode): void
|
||||
}>()
|
||||
|
||||
|
||||
const vVisible = useVModel(props, 'visible', emits)
|
||||
|
||||
const baseCreateMode = useVModel(props, 'baseCreateMode', emits)
|
||||
@@ -29,42 +28,58 @@ const { isTemplatesFeatureEnabled } = storeToRefs(workspaceStore)
|
||||
const { isAiFeaturesEnabled } = useNocoAi()
|
||||
|
||||
const onClickOption = (mode: NcBaseCreateMode) => {
|
||||
if (isTemplatesFeatureEnabled.value && mode === NcBaseCreateMode.FROM_TEMPLATE) {
|
||||
vVisible.value = false
|
||||
navigateToTemplates()
|
||||
if (isTemplatesFeatureEnabled.value && mode === NcBaseCreateMode.FROM_TEMPLATE) {
|
||||
vVisible.value = false
|
||||
navigateToTemplates()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
baseCreateMode.value = mode
|
||||
return
|
||||
}
|
||||
|
||||
baseCreateMode.value = mode
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (!isAiFeaturesEnabled.value && props.variant === 'modal') {
|
||||
baseCreateMode.value = NcBaseCreateMode.FROM_SCRATCH
|
||||
}
|
||||
if (!isAiFeaturesEnabled.value && props.variant === 'modal') {
|
||||
baseCreateMode.value = NcBaseCreateMode.FROM_SCRATCH
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NcMenu v-if="variant === 'dropdown'" variant="large" data-testid="nc-home-create-new-menu"
|
||||
@click="vVisible = false">
|
||||
<DashboardTreeViewCreateProjectMenuItem v-e="['c:base:create:scratch']" icon="plus" label="From Scratch"
|
||||
subtext="Start with an empty base" @click="onClickOption(NcBaseCreateMode.FROM_SCRATCH)" />
|
||||
<NcMenu v-if="variant === 'dropdown'" variant="large" data-testid="nc-home-create-new-menu" @click="vVisible = false">
|
||||
<DashboardTreeViewCreateProjectMenuItem
|
||||
v-e="['c:base:create:scratch']"
|
||||
icon="plus"
|
||||
label="From Scratch"
|
||||
subtext="Start with an empty base"
|
||||
@click="onClickOption(NcBaseCreateMode.FROM_SCRATCH)"
|
||||
/>
|
||||
|
||||
<DashboardTreeViewCreateProjectMenuItem v-if="isAiFeaturesEnabled" v-e="['c:base:ai:create']"
|
||||
icon="ncAutoAwesome" label="Build with AI" subtext="Pre-built structures for common use cases"
|
||||
@click="onClickOption(NcBaseCreateMode.BUILD_WITH_AI)" />
|
||||
<DashboardTreeViewCreateProjectMenuItem
|
||||
v-if="isAiFeaturesEnabled"
|
||||
v-e="['c:base:ai:create']"
|
||||
icon="ncAutoAwesome"
|
||||
label="Build with AI"
|
||||
subtext="Pre-built structures for common use cases"
|
||||
@click="onClickOption(NcBaseCreateMode.BUILD_WITH_AI)"
|
||||
/>
|
||||
</NcMenu>
|
||||
<div v-else class="flex flex-row gap-6 flex-wrap max-w-[min(80vw,738px)] children:(!w-[230px] !max-w-[230px])">
|
||||
<ProjectActionItem
|
||||
v-e="['c:base:create:scratch']"
|
||||
icon="plus"
|
||||
label="From Scratch"
|
||||
subtext="Start with an empty base"
|
||||
@click="onClickOption(NcBaseCreateMode.FROM_SCRATCH)"
|
||||
/>
|
||||
|
||||
</NcMenu>
|
||||
<div v-else class="flex flex-row gap-6 flex-wrap max-w-[min(80vw,738px)] children:(!w-[230px] !max-w-[230px])">
|
||||
<ProjectActionItem v-e="['c:base:create:scratch']" icon="plus" label="From Scratch"
|
||||
subtext="Start with an empty base" @click="onClickOption(NcBaseCreateMode.FROM_SCRATCH)" />
|
||||
|
||||
<ProjectActionItem v-if="isAiFeaturesEnabled" v-e="['c:base:ai:create']" icon="ncAutoAwesome"
|
||||
label="Build with AI" subtext="Pre-built structures for common use cases"
|
||||
@click="onClickOption(NcBaseCreateMode.BUILD_WITH_AI)" />
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<ProjectActionItem
|
||||
v-if="isAiFeaturesEnabled"
|
||||
v-e="['c:base:ai:create']"
|
||||
icon="ncAutoAwesome"
|
||||
label="Build with AI"
|
||||
subtext="Pre-built structures for common use cases"
|
||||
@click="onClickOption(NcBaseCreateMode.BUILD_WITH_AI)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,57 +1,56 @@
|
||||
<script lang="ts" setup>
|
||||
|
||||
import { NcMenuItem } from "#components"
|
||||
import { NcMenuItem } from '#components'
|
||||
|
||||
interface Props {
|
||||
label: string
|
||||
subtext?: string
|
||||
icon?: IconMapKey
|
||||
variant?: 'dropdown' | 'modal'
|
||||
label: string
|
||||
subtext?: string
|
||||
icon?: IconMapKey
|
||||
variant?: 'dropdown' | 'modal'
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
})
|
||||
|
||||
withDefaults(defineProps<Props>(), {})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component :is="variant === 'modal' ? 'div' : NcMenuItem"
|
||||
:inner-class="`w-full ${$slots.subtext || subtext ? '!items-start' : ''}`"
|
||||
:class="`nc-create-project-menu-item-${variant}`">
|
||||
<div class="nc-icon-wrapper">
|
||||
<slot name="icon">
|
||||
<GeneralIcon v-if="icon" :icon="icon" class="h-4 w-4 flex-none" />
|
||||
</slot>
|
||||
</div>
|
||||
<component
|
||||
:is="variant === 'modal' ? 'div' : NcMenuItem"
|
||||
:inner-class="`w-full ${$slots.subtext || subtext ? '!items-start' : ''}`"
|
||||
:class="`nc-create-project-menu-item-${variant}`"
|
||||
>
|
||||
<div class="nc-icon-wrapper">
|
||||
<slot name="icon">
|
||||
<GeneralIcon v-if="icon" :icon="icon" class="h-4 w-4 flex-none" />
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
<div class="nc-content-wrapper">
|
||||
<div class="nc-content-label">
|
||||
<slot name="label">{{ label }}</slot>
|
||||
</div>
|
||||
<div v-if="$slots.subtext || subtext" class="nc-content-subtext">
|
||||
<slot name="subtext">{{ subtext }}</slot>
|
||||
</div>
|
||||
</div>
|
||||
</component>
|
||||
<div class="nc-content-wrapper">
|
||||
<div class="nc-content-label">
|
||||
<slot name="label">{{ label }}</slot>
|
||||
</div>
|
||||
<div v-if="$slots.subtext || subtext" class="nc-content-subtext">
|
||||
<slot name="subtext">{{ subtext }}</slot>
|
||||
</div>
|
||||
</div>
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.nc-icon-wrapper {
|
||||
@apply flex items-center justify-center h-5 children:flex-none;
|
||||
@apply flex items-center justify-center h-5 children:flex-none;
|
||||
}
|
||||
|
||||
.nc-create-project-menu-item-modal {
|
||||
@apply font-normal text-sm flex items-start gap-3 mx-1 px-2 py-1 rounded-md hover:bg-nc-bg-gray-light transition-colors cursor-pointer;
|
||||
|
||||
.nc-content-wrapper {
|
||||
.nc-content-label {}
|
||||
@apply font-normal text-sm flex items-start gap-3 mx-1 px-2 py-1 rounded-md hover:bg-nc-bg-gray-light transition-colors cursor-pointer;
|
||||
|
||||
.nc-content-wrapper {
|
||||
.nc-content-label {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nc-content-wrapper {
|
||||
.nc-content-subtext {
|
||||
@apply text-tiny !leading-4 text-nc-content-gray-muted;
|
||||
}
|
||||
.nc-content-subtext {
|
||||
@apply text-tiny !leading-4 text-nc-content-gray-muted;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user