Browse Source

feat(nc-gui): use shared view meta for survey mode and theme config

pull/3669/head
braks 2 years ago committed by Raju Udava
parent
commit
a672ef3040
  1. 97
      packages/nc-gui/components/smartsheet/toolbar/ShareView.vue
  2. 6
      packages/nc-gui/composables/useSharedFormViewStore.ts
  3. 28
      packages/nc-gui/pages/[projectType]/form/[viewId].vue
  4. 59
      packages/nc-gui/pages/[projectType]/form/[viewId]/index.vue
  5. 6
      packages/nc-gui/pages/[projectType]/form/[viewId]/index/survey.vue

97
packages/nc-gui/components/smartsheet/toolbar/ShareView.vue

@ -1,5 +1,6 @@
<script lang="ts" setup>
import { ViewTypes } from 'nocodb-sdk'
import { isString } from '@vueuse/core'
import {
computed,
extractSdkResponseErrorMsg,
@ -15,6 +16,21 @@ import {
useUIPermission,
watch,
} from '#imports'
import type { ThemeConfig } from '~/lib'
interface SharedViewMeta extends Record<string, any> {
surveyMode?: boolean
theme?: Partial<ThemeConfig>
allowCSVDownload?: boolean
}
interface SharedView {
uuid?: string
id: string
password: string | null
type?: ViewTypes
meta: SharedViewMeta
}
const { theme } = useTheme()
@ -36,29 +52,39 @@ let showShareModel = $ref(false)
const passwordProtected = ref(false)
const surveyMode = ref(false)
const withTheme = ref(false)
const shared = ref()
const shared = ref<SharedView>({ id: '', meta: {}, password: null })
const allowCSVDownload = computed({
get() {
return !!(shared.value?.meta && typeof shared.value.meta === 'string' ? JSON.parse(shared.value.meta) : shared.value.meta)
?.allowCSVDownload
},
set(allow) {
shared.value.meta = { allowCSVDownload: allow }
get: () => !!shared.value.meta.allowCSVDownload,
set: (allow) => {
shared.value.meta = { ...shared.value.meta, allowCSVDownload: allow }
saveAllowCSVDownload()
},
})
const surveyMode = computed({
get: () => !!shared.value.meta.surveyMode,
set: (survey) => {
shared.value.meta = { ...shared.value.meta, surveyMode: survey }
saveSurveyMode()
},
})
const viewTheme = computed({
get: () => !!shared.value.meta.theme,
set: (hasTheme) => {
shared.value.meta = { ...shared.value.meta, theme: hasTheme ? { ...theme.value } : undefined }
saveTheme()
},
})
const genShareLink = async () => {
if (!view.value?.id) return
shared.value = await $api.dbViewShare.create(view.value.id)
shared.value.meta =
shared.value.meta && typeof shared.value.meta === 'string' ? JSON.parse(shared.value.meta) : shared.value.meta
const response = (await $api.dbViewShare.create(view.value.id)) as SharedView
const meta = isString(response.meta) ? JSON.parse(response.meta) : response.meta
shared.value = { ...response, meta }
passwordProtected.value = shared.value.password !== null && shared.value.password !== ''
@ -80,24 +106,27 @@ const sharedViewUrl = computed(() => {
viewType = 'view'
}
let url = `${dashboardUrl?.value}#/nc/${viewType}/${shared.value.uuid}`
return `${dashboardUrl?.value}#/nc/${viewType}/${shared.value.uuid}`
})
/** if survey mode is enabled, append survey path segment */
if (surveyMode.value) {
url = `${url}/survey`
}
async function saveAllowCSVDownload() {
await updateSharedViewMeta()
$e(`a:view:share:${allowCSVDownload.value ? 'enable' : 'disable'}-csv-download`)
}
/** if theme is enabled, append theme query params */
if (withTheme.value) {
url = `${url}?theme=${theme.value.primaryColor.replace('#', '')},${theme.value.accentColor.replace('#', '')}`
}
async function saveSurveyMode() {
await updateSharedViewMeta()
$e(`a:view:share:${surveyMode.value ? 'enable' : 'disable'}-survey-mode`)
}
return url
})
async function saveTheme() {
await updateSharedViewMeta()
$e(`a:view:share:${viewTheme.value ? 'enable' : 'disable'}-theme`)
}
async function saveAllowCSVDownload() {
async function updateSharedViewMeta() {
try {
const meta = shared.value.meta && typeof shared.value.meta === 'string' ? JSON.parse(shared.value.meta) : shared.value.meta
const meta = shared.value.meta && isString(shared.value.meta) ? JSON.parse(shared.value.meta) : shared.value.meta
await $api.dbViewShare.update(shared.value.id, {
meta,
@ -108,14 +137,12 @@ async function saveAllowCSVDownload() {
message.error(await extractSdkResponseErrorMsg(e))
}
if (allowCSVDownload.value) {
$e('a:view:share:enable-csv-download')
} else {
$e('a:view:share:disable-csv-download')
}
return true
}
const saveShareLinkPassword = async () => {
if (!shared.value.password) return
try {
await $api.dbViewShare.update(shared.value.id, {
password: shared.value.password,
@ -129,9 +156,9 @@ const saveShareLinkPassword = async () => {
$e('a:view:share:enable-pwd')
}
const copyLink = () => {
const copyLink = async () => {
if (sharedViewUrl.value) {
copy(sharedViewUrl.value)
await copy(sharedViewUrl.value)
// Copied to clipboard
message.success(t('msg.info.copiedToClipboard'))
@ -195,7 +222,7 @@ watch(passwordProtected, (value) => {
<div>
<!-- todo: i18n -->
<a-checkbox v-model:checked="withTheme" class="!text-xs"> Use Theme </a-checkbox>
<a-checkbox v-model:checked="viewTheme" class="!text-xs"> Use Theme </a-checkbox>
</div>
<div>

6
packages/nc-gui/composables/useSharedFormViewStore.ts

@ -3,6 +3,7 @@ import { minLength, required } from '@vuelidate/validators'
import type { Ref } from 'vue'
import type { ColumnType, FormType, LinkToAnotherRecordType, TableType, ViewType } from 'nocodb-sdk'
import { ErrorMessages, RelationTypes, UITypes, isVirtualCol } from 'nocodb-sdk'
import { isString } from '@vueuse/core'
import {
SharedViewPasswordInj,
computed,
@ -31,6 +32,7 @@ const [useProvideSharedFormStore, useSharedFormStore] = useInjectionState((share
const sharedFormView = ref<FormType>()
const meta = ref<TableType>()
const columns = ref<(ColumnType & { required?: boolean; show?: boolean; label?: string })[]>()
const sharedViewMeta = ref<any>({})
const { api, isLoading } = useApi()
@ -65,6 +67,9 @@ const [useProvideSharedFormStore, useSharedFormStore] = useInjectionState((share
meta.value = viewMeta.model
columns.value = viewMeta.model?.columns
const _sharedViewMeta = (viewMeta as any).meta
sharedViewMeta.value = isString(_sharedViewMeta) ? JSON.parse(_sharedViewMeta) : _sharedViewMeta
await setMeta(viewMeta.model)
const relatedMetas = { ...viewMeta.relatedMetas }
@ -205,6 +210,7 @@ const [useProvideSharedFormStore, useSharedFormStore] = useInjectionState((share
secondsRemain,
passwordDlg,
isLoading,
sharedViewMeta,
}
}, 'expanded-form-store')

28
packages/nc-gui/pages/[projectType]/form/[viewId].vue

@ -22,7 +22,9 @@ useSidebar('nc-left-sidebar', { hasSidebar: false })
const route = useRoute()
const { loadSharedView, sharedView, meta, notFound } = useProvideSharedFormStore(route.params.viewId as string)
const { loadSharedView, sharedView, meta, notFound, password, passwordDlg } = useProvideSharedFormStore(
route.params.viewId as string,
)
await loadSharedView()
@ -39,6 +41,30 @@ if (!notFound.value) {
<template>
<NuxtLayout>
<NuxtPage />
<a-modal
v-model:visible="passwordDlg"
:closable="false"
width="28rem"
centered
:footer="null"
:mask-closable="false"
wrap-class-name="nc-modal-shared-form-password-dlg"
@close="passwordDlg = false"
>
<div class="w-full flex flex-col">
<a-typography-title :level="4">This shared view is protected</a-typography-title>
<a-form ref="formRef" :model="{ password }" class="mt-2" @finish="passwordDlg = false">
<a-form-item name="password" :rules="[{ required: true, message: $t('msg.error.signUpRules.passwdRequired') }]">
<a-input-password v-model:value="password" :placeholder="$t('msg.info.signUp.enterPassword')" />
</a-form-item>
<!-- Unlock -->
<a-button type="primary" html-type="submit">{{ $t('general.unlock') }}</a-button>
</a-form>
</div>
</a-modal>
</NuxtLayout>
</template>

59
packages/nc-gui/pages/[projectType]/form/[viewId]/index.vue

@ -1,25 +1,20 @@
<script setup lang="ts">
import { useDark, useRoute, useSharedFormStoreOrThrow, useTheme, watch } from '#imports'
import { navigateTo, useDark, useRoute, useRouter, useSharedFormStoreOrThrow, useTheme, watch } from '#imports'
const { passwordDlg, password, loadSharedView } = useSharedFormStoreOrThrow()
const route = useRoute()
const { sharedViewMeta } = useSharedFormStoreOrThrow()
const isDark = useDark()
const { setTheme } = useTheme()
const route = useRoute()
const router = useRouter()
watch(
() => route.query.theme,
() => sharedViewMeta.value.theme,
(nextTheme) => {
if (nextTheme) {
const theme = (nextTheme as string).split(',').map((t) => t.trim() && `#${t}`)
setTheme({
primaryColor: theme[0],
accentColor: theme[1],
})
}
if (nextTheme) setTheme(nextTheme)
},
{ immediate: true },
)
@ -27,6 +22,20 @@ watch(
const onClick = () => {
isDark.value = !isDark.value
}
const shouldRedirect = (to: string) => {
if (sharedViewMeta.value.surveyMode) {
if (!to.includes('survey')) navigateTo(`/nc/form/${route.params.viewId}/survey`)
} else {
navigateTo(`/nc/form/${route.params.viewId}`)
}
}
shouldRedirect(route.name as string)
router.afterEach((to) => {
shouldRedirect(to.name as string)
})
</script>
<template>
@ -48,30 +57,6 @@ const onClick = () => {
<MaterialSymbolsLightModeOutline v-else />
</Transition>
</div>
<a-modal
v-model:visible="passwordDlg"
:closable="false"
width="28rem"
centered
:footer="null"
:mask-closable="false"
wrap-class-name="nc-modal-shared-form-password-dlg"
@close="passwordDlg = false"
>
<div class="w-full flex flex-col">
<a-typography-title :level="4">This shared view is protected</a-typography-title>
<a-form ref="formRef" :model="{ password }" class="mt-2" @finish="loadSharedView">
<a-form-item name="password" :rules="[{ required: true, message: $t('msg.error.signUpRules.passwdRequired') }]">
<a-input-password v-model:value="password" :placeholder="$t('msg.info.signUp.enterPassword')" />
</a-form-item>
<!-- Unlock -->
<a-button type="primary" html-type="submit">{{ $t('general.unlock') }}</a-button>
</a-form>
</div>
</a-modal>
</div>
</template>

6
packages/nc-gui/pages/[projectType]/form/[viewId]/index/survey.vue

@ -136,7 +136,7 @@ onKeyStroke(['ArrowRight', 'ArrowUp', 'Enter', 'Space'], goNext)
<div ref="el" class="w-full min-h-2/3 grid grid-rows-2">
<template v-if="sharedFormView">
<div class="max-w-[max(33%,600px)] m-auto flex flex-col justify-end">
<h1 class="prose-2xl font-bold my-4">{{ sharedFormView.heading }}</h1>
<h1 class="prose-2xl font-bold self-center my-4">{{ sharedFormView.heading }}</h1>
<h2 class="prose-lg text-slate-500 dark:text-slate-300 self-center mb-4">
{{ sharedFormView.subheading }}
@ -226,7 +226,9 @@ onKeyStroke(['ArrowRight', 'ArrowUp', 'Enter', 'Space'], goNext)
</a-tooltip>
<!-- todo: i18n -->
<div class="text-sm flex items-center gap-1">Press Enter <MaterialSymbolsKeyboardReturn class="mt-1" /></div>
<div class="text-sm text-gray-500 flex items-center gap-1">
Press Enter <MaterialSymbolsKeyboardReturn class="mt-1" />
</div>
</div>
</div>
</div>

Loading…
Cancel
Save