mirror of https://github.com/nocodb/nocodb
Pranav C
3 months ago
9 changed files with 384 additions and 0 deletions
@ -0,0 +1,62 @@ |
|||||||
|
<script setup lang="ts"> |
||||||
|
const { t } = useI18n() |
||||||
|
|
||||||
|
const configs = ref([ |
||||||
|
{ |
||||||
|
title: t('labels.setupLabel', { label: t('labels.email') }), |
||||||
|
key: 'email', |
||||||
|
description: 'Configure an email account to send system notifications to your organisation’s users.', |
||||||
|
docsLink: '', |
||||||
|
path: '/account/setup/email' |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: t('labels.setupLabel', { label: t('labels.storage') }), |
||||||
|
key: 'storage', |
||||||
|
description: 'Configure a storage service to store your organisation’s data.', |
||||||
|
docsLink: '', |
||||||
|
path: '/account/setup/storage' |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: t('labels.switchToProd'), |
||||||
|
key: 'switchToProd', |
||||||
|
description: 'Configure a production-ready app database to port from the existing built-in application database.', |
||||||
|
docsLink: '' |
||||||
|
}, |
||||||
|
]) |
||||||
|
</script> |
||||||
|
|
||||||
|
<template> |
||||||
|
<div class="flex flex-col" data-test-id="nc-setup"> |
||||||
|
<NcPageHeader> |
||||||
|
<template #icon> |
||||||
|
<div class="flex justify-center items-center h-5 w-5"> |
||||||
|
<GeneralIcon icon="ncSliders" class="flex-none text-[20px]" /> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
<template #title> |
||||||
|
<span data-rec="true"> |
||||||
|
{{ $t('labels.setup') }} |
||||||
|
</span> |
||||||
|
</template> |
||||||
|
</NcPageHeader> |
||||||
|
|
||||||
|
<div |
||||||
|
class="nc-content-max-w flex-1 max-h-[calc(100vh_-_100px)] overflow-y-auto nc-scrollbar-thin flex flex-col items-center gap-6 p-6" |
||||||
|
> |
||||||
|
<div class="flex flex-col gap-6 w-150"> |
||||||
|
<div v-for="config of configs" class="flex flex-col border-1 rounded-2xl border-gray-200 p-6 gap-y-2"> |
||||||
|
<div class="flex font-bold text-base" data-rec="true">{{ config.title }}</div> |
||||||
|
<div class="text-gray-600 text-tiny">{{config.description}}</div> |
||||||
|
|
||||||
|
<div class="flex justify-between"> |
||||||
|
<NcButton size="small !text-tiny" type="text">Go to docs</NcButton> |
||||||
|
<NcButton size="small" @click="navigateTo(config.path)">Configure</NcButton> |
||||||
|
|
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<style scoped lang="scss"></style> |
@ -0,0 +1,115 @@ |
|||||||
|
<script setup lang="ts"> |
||||||
|
|
||||||
|
const { emailApps } = useAccountSetupStoreOrThrow() |
||||||
|
</script> |
||||||
|
|
||||||
|
<template> |
||||||
|
<div class="w-full"> |
||||||
|
<div class="p-4 flex flex-wrap w-full gap-5 mx-auto my-2"> |
||||||
|
<div v-for="(app) in emailApps" :key="app.title" class="w-296px max-w-296px flex gap-6 border-1 border-gray-100 py-3 px-6 rounded items-center"> |
||||||
|
<img |
||||||
|
v-if="app.title !== 'SMTP'" |
||||||
|
class="max-w-32px max-h-32px" |
||||||
|
alt="logo" |
||||||
|
:style="{ |
||||||
|
backgroundColor: app.title === 'SES' ? '#242f3e' : '', |
||||||
|
}" |
||||||
|
:src="app.logo" |
||||||
|
/> |
||||||
|
<GeneralIcon v-else icon="mail" /> |
||||||
|
<span class="font-weight-bold">{{ app.title }}</span> |
||||||
|
</div> |
||||||
|
<!-- |
||||||
|
<a-card |
||||||
|
v-for="(app, i) in emailApps" |
||||||
|
:key="i" |
||||||
|
class="sm:w-100 md:w-130" |
||||||
|
:class="`relative flex overflow-x-hidden app-item-card !shadow-sm rounded-md w-full nc-app-store-card-${app.title}`" |
||||||
|
> |
||||||
|
<div class="install-btn flex flex-row justify-end space-x-1"> |
||||||
|
<a-button v-if="app.parsedInput" size="small" type="primary" @click="showInstallPluginModal(app)"> |
||||||
|
<div class="flex flex-row justify-center items-center caption capitalize nc-app-store-card-edit"> |
||||||
|
<IcRoundEdit class="pr-0.5" :height="12" /> |
||||||
|
{{ $t('general.edit') }} |
||||||
|
</div> |
||||||
|
</a-button> |
||||||
|
|
||||||
|
<a-button v-if="app.parsedInput" size="small" outlined @click="showResetPluginModal(app)"> |
||||||
|
<div class="flex flex-row justify-center items-center caption capitalize nc-app-store-card-reset"> |
||||||
|
<component :is="iconMap.closeCircle" /> |
||||||
|
<div class="flex ml-0.5">{{ $t('general.reset') }}</div> |
||||||
|
</div> |
||||||
|
</a-button> |
||||||
|
|
||||||
|
<a-button v-else size="small" type="primary" @click="showInstallPluginModal(app)"> |
||||||
|
<div class="flex flex-row justify-center items-center caption capitalize nc-app-store-card-install"> |
||||||
|
<component :is="iconMap.plus" /> |
||||||
|
{{ $t('general.install') }} |
||||||
|
</div> |
||||||
|
</a-button> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="flex flex-row space-x-2 items-center justify-start w-full"> |
||||||
|
<div class="flex w-[68px]"> |
||||||
|
<img |
||||||
|
v-if="app.title !== 'SMTP'" |
||||||
|
class="avatar" |
||||||
|
alt="logo" |
||||||
|
:style="{ |
||||||
|
backgroundColor: app.title === 'SES' ? '#242f3e' : '', |
||||||
|
}" |
||||||
|
:src="app.logo" |
||||||
|
/> |
||||||
|
|
||||||
|
<div v-else /> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="flex flex-col flex-1 w-3/5 pl-3"> |
||||||
|
<a-typography-title :level="5">{{ app.title }}</a-typography-title> |
||||||
|
|
||||||
|
{{ app.description }} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</a-card>--> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<style scoped lang="scss"> |
||||||
|
.app-item-card { |
||||||
|
position: relative; |
||||||
|
transition: 0.4s background-color; |
||||||
|
|
||||||
|
.install-btn { |
||||||
|
position: absolute; |
||||||
|
opacity: 1; |
||||||
|
right: -100%; |
||||||
|
top: 10px; |
||||||
|
transition: 0.4s opacity, 0.4s right; |
||||||
|
} |
||||||
|
|
||||||
|
&:hover .install-btn { |
||||||
|
right: 10px; |
||||||
|
opacity: 1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.app-item-card { |
||||||
|
transition: 0.4s background-color, 0.4s transform; |
||||||
|
|
||||||
|
&:hover { |
||||||
|
background: rgba(123, 126, 136, 0.1) !important; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.caption { |
||||||
|
font-size: 0.7rem; |
||||||
|
} |
||||||
|
|
||||||
|
.avatar { |
||||||
|
width: 5rem; |
||||||
|
height: 5rem; |
||||||
|
padding: 0.25rem; |
||||||
|
object-fit: contain; |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,114 @@ |
|||||||
|
<script setup lang="ts"> |
||||||
|
const { storageApps } = useAccountSetupStoreOrThrow() |
||||||
|
</script> |
||||||
|
|
||||||
|
<template> |
||||||
|
<div class="w-full"> |
||||||
|
<div class="p-4 flex flex-wrap w-full gap-5 mx-auto my-2"> |
||||||
|
<div v-for="(app) in storageApps" :key="app.title" class="w-296px max-w-296px flex gap-6 border-1 border-gray-100 py-3 px-6 rounded items-center"> |
||||||
|
<img |
||||||
|
v-if="app.title !== 'SMTP'" |
||||||
|
class="max-w-32px max-h-32px" |
||||||
|
alt="logo" |
||||||
|
:style="{ |
||||||
|
backgroundColor: app.title === 'SES' ? '#242f3e' : '', |
||||||
|
}" |
||||||
|
:src="app.logo" |
||||||
|
/> |
||||||
|
<GeneralIcon v-else icon="mail" /> |
||||||
|
<span class="font-weight-bold">{{ app.title }}</span> |
||||||
|
</div> |
||||||
|
<!-- |
||||||
|
<a-card |
||||||
|
v-for="(app, i) in storageApps" |
||||||
|
:key="i" |
||||||
|
class="sm:w-100 md:w-130" |
||||||
|
:class="`relative flex overflow-x-hidden app-item-card !shadow-sm rounded-md w-full nc-app-store-card-${app.title}`" |
||||||
|
> |
||||||
|
<div class="install-btn flex flex-row justify-end space-x-1"> |
||||||
|
<a-button v-if="app.parsedInput" size="small" type="primary" @click="showInstallPluginModal(app)"> |
||||||
|
<div class="flex flex-row justify-center items-center caption capitalize nc-app-store-card-edit"> |
||||||
|
<IcRoundEdit class="pr-0.5" :height="12" /> |
||||||
|
{{ $t('general.edit') }} |
||||||
|
</div> |
||||||
|
</a-button> |
||||||
|
|
||||||
|
<a-button v-if="app.parsedInput" size="small" outlined @click="showResetPluginModal(app)"> |
||||||
|
<div class="flex flex-row justify-center items-center caption capitalize nc-app-store-card-reset"> |
||||||
|
<component :is="iconMap.closeCircle" /> |
||||||
|
<div class="flex ml-0.5">{{ $t('general.reset') }}</div> |
||||||
|
</div> |
||||||
|
</a-button> |
||||||
|
|
||||||
|
<a-button v-else size="small" type="primary" @click="showInstallPluginModal(app)"> |
||||||
|
<div class="flex flex-row justify-center items-center caption capitalize nc-app-store-card-install"> |
||||||
|
<component :is="iconMap.plus" /> |
||||||
|
{{ $t('general.install') }} |
||||||
|
</div> |
||||||
|
</a-button> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="flex flex-row space-x-2 items-center justify-start w-full"> |
||||||
|
<div class="flex w-[68px]"> |
||||||
|
<img |
||||||
|
v-if="app.title !== 'SMTP'" |
||||||
|
class="avatar" |
||||||
|
alt="logo" |
||||||
|
:style="{ |
||||||
|
backgroundColor: app.title === 'SES' ? '#242f3e' : '', |
||||||
|
}" |
||||||
|
:src="app.logo" |
||||||
|
/> |
||||||
|
|
||||||
|
<div v-else /> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="flex flex-col flex-1 w-3/5 pl-3"> |
||||||
|
<a-typography-title :level="5">{{ app.title }}</a-typography-title> |
||||||
|
|
||||||
|
{{ app.description }} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</a-card>--> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<style scoped lang="scss"> |
||||||
|
.app-item-card { |
||||||
|
position: relative; |
||||||
|
transition: 0.4s background-color; |
||||||
|
|
||||||
|
.install-btn { |
||||||
|
position: absolute; |
||||||
|
opacity: 1; |
||||||
|
right: -100%; |
||||||
|
top: 10px; |
||||||
|
transition: 0.4s opacity, 0.4s right; |
||||||
|
} |
||||||
|
|
||||||
|
&:hover .install-btn { |
||||||
|
right: 10px; |
||||||
|
opacity: 1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.app-item-card { |
||||||
|
transition: 0.4s background-color, 0.4s transform; |
||||||
|
|
||||||
|
&:hover { |
||||||
|
background: rgba(123, 126, 136, 0.1) !important; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.caption { |
||||||
|
font-size: 0.7rem; |
||||||
|
} |
||||||
|
|
||||||
|
.avatar { |
||||||
|
width: 5rem; |
||||||
|
height: 5rem; |
||||||
|
padding: 0.25rem; |
||||||
|
object-fit: contain; |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,46 @@ |
|||||||
|
import rfdc from 'rfdc' |
||||||
|
import type { ColumnReqType, ColumnType, PluginType, TableType } from 'nocodb-sdk' |
||||||
|
import { UITypes, isLinksOrLTAR } from 'nocodb-sdk' |
||||||
|
import type { Ref } from 'vue' |
||||||
|
import type { RuleObject } from 'ant-design-vue/es/form' |
||||||
|
import { generateUniqueColumnName } from '~/helpers/parsers/parserHelpers' |
||||||
|
|
||||||
|
const [useProvideAccountSetupStore, useAccountSetupStore] = createInjectionState(() => { |
||||||
|
const apps = ref<(PluginType & { parsedInput?: Record<string, any>; tags?: string[] })[]>([]) |
||||||
|
|
||||||
|
const { $api, $e } = useNuxtApp() |
||||||
|
|
||||||
|
const emailApps = computed(() => apps.value.filter((app) => app.category === 'Email')) |
||||||
|
const storageApps = computed(() => apps.value.filter((app) => app.category === ('Storage'))) |
||||||
|
|
||||||
|
const loadSetupApps = async () => { |
||||||
|
try { |
||||||
|
const plugins = (await $api.plugin.list()).list ?? [] |
||||||
|
|
||||||
|
apps.value = plugins.map((p) => ({ |
||||||
|
...p, |
||||||
|
tags: p.tags ? p.tags.split(',') : [], |
||||||
|
parsedInput: p.input && JSON.parse(p.input as string), |
||||||
|
})) as any[] |
||||||
|
} catch (e: any) { |
||||||
|
message.error(await extractSdkResponseErrorMsg(e)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
apps, |
||||||
|
emailApps, |
||||||
|
storageApps, |
||||||
|
loadSetupApps, |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
export { useProvideAccountSetupStore } |
||||||
|
|
||||||
|
export function useAccountSetupStoreOrThrow() { |
||||||
|
const columnCreateStore = useAccountSetupStore() |
||||||
|
|
||||||
|
if (columnCreateStore == null) throw new Error('Please call `useProvideAccountSetupStore` on the appropriate parent component') |
||||||
|
|
||||||
|
return columnCreateStore |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
<script setup lang="ts"> |
||||||
|
const { loadSetupApps } = useProvideAccountSetupStore() |
||||||
|
|
||||||
|
onMounted(async () => { |
||||||
|
await loadSetupApps() |
||||||
|
}) |
||||||
|
</script> |
||||||
|
|
||||||
|
<template> |
||||||
|
<div class="h-full"> |
||||||
|
<template v-if="$route.params.nestedPage === 'storage'"> |
||||||
|
<LazyAccountSetupStorage /> |
||||||
|
</template> |
||||||
|
<template v-else-if="$route.params.nestedPage === 'email'"> |
||||||
|
<LazyAccountSetupEmail /> |
||||||
|
</template> |
||||||
|
<template v-else> |
||||||
|
<LazyAccountSetup /> |
||||||
|
</template> |
||||||
|
</div> |
||||||
|
</template> |
Loading…
Reference in new issue