Browse Source

fix(nc-gui): shared form oss ui changes

pull/7664/head
Ramesh Mane 8 months ago
parent
commit
8f2a6a56be
  1. 72
      packages/nc-gui/pages/index/[typeOrId]/form/[viewId]/index.vue
  2. 145
      packages/nc-gui/pages/index/[typeOrId]/form/[viewId]/index/index.vue
  3. 127
      packages/nc-gui/pages/index/[typeOrId]/form/[viewId]/index/survey.vue

72
packages/nc-gui/pages/index/[typeOrId]/form/[viewId]/index.vue

@ -1,27 +1,22 @@
<script setup lang="ts"> <script setup lang="ts">
import { navigateTo, useDark, useRoute, useRouter, useSharedFormStoreOrThrow, useTheme, watch } from '#imports' import { navigateTo, useDark, useRoute, useRouter, useSharedFormStoreOrThrow } from '#imports'
const { sharedViewMeta } = useSharedFormStoreOrThrow() const { sharedViewMeta, sharedFormView } = useSharedFormStoreOrThrow()
const isDark = useDark() const isDark = useDark()
const { setTheme } = useTheme()
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
watch( // For now dark theme is disabled
() => sharedViewMeta.value.withTheme, // const onClick = () => {
(hasTheme) => { // isDark.value = !isDark.value
if (hasTheme && sharedViewMeta.value.theme) setTheme(sharedViewMeta.value.theme) // }
},
{ immediate: true },
)
const onClick = () => { onMounted(() => {
isDark.value = !isDark.value isDark.value = false
} })
const shouldRedirect = (to: string) => { const shouldRedirect = (to: string) => {
if (sharedViewMeta.value.surveyMode) { if (sharedViewMeta.value.surveyMode) {
@ -38,11 +33,14 @@ router.afterEach((to) => shouldRedirect(to.name as string))
<template> <template>
<div <div
class="scrollbar-thin-dull overflow-y-auto overflow-x-hidden flex flex-col color-transition nc-form-view relative bg-primary bg-opacity-10 dark:(bg-slate-900) h-[100vh] min-h-[600px] py-4" class="scrollbar-thin-dull h-[100vh] overflow-y-auto overflow-x-hidden flex flex-col color-transition p-4 lg:p-10 nc-form-view relative min-h-[600px]"
:style="{
background: parseProp(sharedFormView?.meta)?.background_color || '#F9F9FA',
}"
> >
<NuxtPage /> <NuxtPage />
<div <!-- <div
class="color-transition flex items-center justify-center cursor-pointer absolute top-4 md:top-15 right-4 md:right-15 rounded-full p-2 bg-white dark:(bg-slate-600) shadow hover:(ring-1 ring-accent ring-opacity-100)" class="color-transition flex items-center justify-center cursor-pointer absolute top-4 md:top-15 right-4 md:right-15 rounded-full p-2 bg-white dark:(bg-slate-600) shadow hover:(ring-1 ring-accent ring-opacity-100)"
@click="onClick" @click="onClick"
> >
@ -50,7 +48,7 @@ router.afterEach((to) => shouldRedirect(to.name as string))
<MaterialSymbolsDarkModeOutline v-if="isDark" /> <MaterialSymbolsDarkModeOutline v-if="isDark" />
<MaterialSymbolsLightModeOutline v-else /> <MaterialSymbolsLightModeOutline v-else />
</Transition> </Transition>
</div> </div> -->
</div> </div>
</template> </template>
@ -68,10 +66,6 @@ p {
} }
.nc-form-view { .nc-form-view {
.nc-data-cell {
@apply border-solid border-1 !border-gray-300 dark:!border-slate-200;
}
.nc-cell { .nc-cell {
@apply bg-white dark:bg-slate-500; @apply bg-white dark:bg-slate-500;
@ -95,15 +89,7 @@ p {
@apply bg-white dark:bg-slate-500; @apply bg-white dark:bg-slate-500;
&.nc-input { &.nc-input {
@apply w-full px-3 min-h-[40px] flex items-center; @apply w-full rounded p-2 min-h-[40px] flex items-center;
&.nc-cell-longtext {
@apply !px-1;
}
&.nc-cell-json {
@apply !h-auto;
}
.duration-cell-wrapper { .duration-cell-wrapper {
@apply w-full; @apply w-full;
@ -132,11 +118,37 @@ p {
} }
} }
&.nc-cell-longtext {
@apply p-0 h-auto;
& > div {
@apply w-full;
}
:deep(textarea) {
@apply !p-2;
&:focus {
box-shadow: none !important;
}
}
}
&:not(.nc-cell-longtext) {
@apply px-2 py-2;
}
textarea { textarea {
@apply px-4 py-2 rounded;
&:focus { &:focus {
box-shadow: none !important; box-shadow: none !important;
} }
} }
&.nc-cell-json {
@apply h-auto;
& > div {
@apply w-full;
}
}
} }
} }

145
packages/nc-gui/pages/index/[typeOrId]/form/[viewId]/index/index.vue

@ -3,10 +3,21 @@ import type { ColumnType } from 'nocodb-sdk'
import { RelationTypes, UITypes, isVirtualCol } from 'nocodb-sdk' import { RelationTypes, UITypes, isVirtualCol } from 'nocodb-sdk'
import { ref } from 'vue' import { ref } from 'vue'
import { StreamBarcodeReader } from 'vue-barcode-reader' import { StreamBarcodeReader } from 'vue-barcode-reader'
import { iconMap, useGlobal, useSharedFormStoreOrThrow } from '#imports' import { iconMap, useSharedFormStoreOrThrow } from '#imports'
const { sharedFormView, submitForm, v$, formState, notFound, formColumns, submitted, secondsRemain, isLoading } = const {
useSharedFormStoreOrThrow() sharedFormView,
submitForm,
clearForm,
v$,
formState,
notFound,
formColumns,
submitted,
secondsRemain,
isLoading,
progress,
} = useSharedFormStoreOrThrow()
function isRequired(_columnObj: Record<string, any>, required = false) { function isRequired(_columnObj: Record<string, any>, required = false) {
let columnObj = _columnObj let columnObj = _columnObj
@ -21,8 +32,6 @@ function isRequired(_columnObj: Record<string, any>, required = false) {
return !!(required || (columnObj && columnObj.rqd && !columnObj.cdf)) return !!(required || (columnObj && columnObj.rqd && !columnObj.cdf))
} }
const { isMobileMode } = useGlobal()
const fieldTitleForCurrentScan = ref('') const fieldTitleForCurrentScan = ref('')
const scannerIsReady = ref(false) const scannerIsReady = ref(false)
@ -70,32 +79,37 @@ const onDecode = async (scannedCodeValue: string) => {
</script> </script>
<template> <template>
<div class="h-full flex flex-col items-center" :class="isMobileMode ? 'mobile' : 'desktop'"> <div class="h-full flex flex-col items-center">
<GeneralFormBanner
v-if="sharedFormView"
:banner-image-url="sharedFormView.banner_image_url"
class="flex-none dark:border-none"
/>
<div <div
class="color-transition flex flex-col justify-center gap-2 w-full max-w-[max(33%,600px)] m-auto py-4 pb-8 px-16 md:(bg-white dark:bg-slate-700 rounded-lg border-1 border-gray-200 shadow-xl)" class="transition-all duration-300 ease-in relative flex flex-col justify-center gap-2 w-full max-w-[max(33%,688px)] mx-auto my-6 bg-white dark:bg-transparent rounded-3xl border-1 border-gray-200 px-4 py-8 lg:p-12 md:(p-8 dark:bg-slate-700)"
> >
<template v-if="sharedFormView"> <template v-if="sharedFormView">
<h1 class="prose-2xl font-bold self-center my-4 break-words"> <div class="mb-4">
{{ sharedFormView.heading }} <h1 class="text-2xl font-bold text-gray-900 mb-4">
</h1> {{ sharedFormView.heading }}
</h1>
<h2 <h2 v-if="sharedFormView.subheading" class="text-base font-bold text-gray-500 dark:text-slate-300 mb-4">
v-if="sharedFormView.subheading" {{ sharedFormView.subheading }}
class="prose-lg text-slate-500 dark:text-slate-300 self-center mb-4 leading-6 break-words" </h2>
> </div>
{{ sharedFormView.subheading }}
</h2>
<a-alert v-if="notFound" type="warning" class="my-4 text-center" :message="$t('general.notFound')" /> <a-alert v-if="notFound" type="warning" class="my-4 text-center" message="Not found" />
<template v-else-if="submitted"> <template v-else-if="submitted">
<div class="flex justify-center"> <div class="flex justify-center">
<div v-if="sharedFormView" class="min-w-350px mt-3"> <div v-if="sharedFormView" class="w-full lg:w-[95%]">
<a-alert <a-alert
type="success" type="success"
class="my-4 text-center" class="!my-4 text-center !rounded-lg"
outlined outlined
:message="sharedFormView.success_msg || $t('msg.successfullySubmittedFormData')" :message="sharedFormView.success_msg || 'Successfully submitted form data'"
/> />
<p v-if="sharedFormView.show_blank_form" class="text-xs text-slate-500 dark:text-slate-300 text-center my-4"> <p v-if="sharedFormView.show_blank_form" class="text-xs text-slate-500 dark:text-slate-300 text-center my-4">
@ -103,7 +117,9 @@ const onDecode = async (scannedCodeValue: string) => {
</p> </p>
<div v-if="sharedFormView.submit_another_form" class="text-center"> <div v-if="sharedFormView.submit_another_form" class="text-center">
<a-button type="primary" @click="submitted = false"> {{ $t('activity.submitAnotherForm') }}</a-button> <NcButton type="primary" size="medium" @click="submitted = false">
{{ $t('activity.submitAnotherForm') }}</NcButton
>
</div> </div>
</div> </div>
</div> </div>
@ -124,7 +140,7 @@ const onDecode = async (scannedCodeValue: string) => {
<StreamBarcodeReader v-show="scannerIsReady" @decode="onDecode" @loaded="onLoaded"> </StreamBarcodeReader> <StreamBarcodeReader v-show="scannerIsReady" @decode="onDecode" @loaded="onLoaded"> </StreamBarcodeReader>
</div> </div>
</a-modal> </a-modal>
<GeneralOverlay class="bg-gray-400/75" :model-value="isLoading" inline transition> <GeneralOverlay class="bg-gray-50/75 rounded-3xl" :model-value="isLoading" inline transition>
<div class="w-full h-full flex items-center justify-center"> <div class="w-full h-full flex items-center justify-center">
<a-spin size="large" /> <a-spin size="large" />
</div> </div>
@ -132,22 +148,16 @@ const onDecode = async (scannedCodeValue: string) => {
<div class="nc-form-wrapper"> <div class="nc-form-wrapper">
<div class="nc-form h-full"> <div class="nc-form h-full">
<div class="flex flex-col gap-6"> <div class="flex flex-col gap-3 md:gap-6">
<div v-for="(field, index) in formColumns" :key="index" class="flex flex-col gap-2"> <div v-for="(field, index) in formColumns" :key="index" class="flex flex-col gap-2">
<div class="flex nc-form-column-label"> <div class="nc-form-column-label">
<LazySmartsheetHeaderVirtualCell <span>
v-if="isVirtualCol(field)" {{ field.label || field.title }}
:column="{ ...field, title: field.label || field.title }" </span>
:required="isRequired(field, field.required)" <span v-if="isRequired(field, field.required)" class="text-red-500">&nbsp;*</span>
:hide-menu="true" </div>
/> <div v-if="field?.description" class="nc-form-column-description text-gray-500 text-sm">
{{ field?.description }}
<LazySmartsheetHeaderCell
v-else
:column="{ ...field, title: field.label || field.title }"
:required="isRequired(field, field.required)"
:hide-menu="true"
/>
</div> </div>
<div> <div>
@ -182,34 +192,59 @@ const onDecode = async (scannedCodeValue: string) => {
</a-button> </a-button>
</LazySmartsheetDivDataCell> </LazySmartsheetDivDataCell>
<div <div class="flex flex-col gap-2 text-slate-500 dark:text-slate-300 text-sm mt-2">
class="flex flex-col gap-2 text-slate-500 dark:text-slate-300 text-[0.75rem] my-2 px-1 leading-[18px]" <template v-if="isVirtualCol(field)">
style="word-break: break-word" <div v-for="error of v$.virtual[field.title]?.$errors" :key="`${error}virtual`" class="text-red-500">
> {{ error.$message }}
<div v-for="error of v$.localState[field.title]?.$errors" :key="error" class="text-red-500"> </div>
{{ error.$message }} </template>
</div> <template v-else>
<div v-for="error of v$.localState[field.title]?.$errors" :key="error" class="text-red-500">
{{ field.description }} {{ error.$message }}
</div>
</template>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="text-center mt-4"> <div class="flex justify-between items-center mt-6">
<NcButton type="primary" html-type="submit" data-testid="shared-form-submit-button" @click="submitForm"> <NcButton
html-type="reset"
type="secondary"
size="small"
:disabled="isLoading"
class="nc-shared-form-button shared-form-clear-button"
data-testid="shared-form-clear-button"
@click="clearForm"
>
{{ $t('activity.clearForm') }}
</NcButton>
<NcButton
html-type="submit"
:disabled="progress"
type="primary"
size="small"
class="nc-shared-form-button shared-form-submit-button"
data-testid="shared-form-submit-button"
@click="submitForm"
>
{{ $t('general.submit') }} {{ $t('general.submit') }}
</NcButton> </NcButton>
</div> </div>
</div> </div>
</div> </div>
<div v-if="!parseProp(sharedFormView?.meta).hide_branding">
<a-divider class="!my-6 !md:my-8" />
<div class="inline-block">
<GeneralFormBranding />
</div>
</div>
</template> </template>
</template> </template>
</div> </div>
<div>&nbsp;</div>
<div class="flex items-end">
<GeneralPoweredBy />
</div>
</div> </div>
</template> </template>
@ -221,4 +256,10 @@ const onDecode = async (scannedCodeValue: string) => {
@apply h-auto; @apply h-auto;
@apply ml-1; @apply ml-1;
} }
.nc-shared-form-button {
&.nc-button.ant-btn:focus {
box-shadow: 0px 0px 0px 2px #fff, 0px 0px 0px 4px #3069fe;
}
}
</style> </style>

127
packages/nc-gui/pages/index/[typeOrId]/form/[viewId]/index/survey.vue

@ -242,13 +242,13 @@ onMounted(() => {
class="max-w-[max(33%,600px)] mx-auto flex flex-col justify-end" class="max-w-[max(33%,600px)] mx-auto flex flex-col justify-end"
> >
<div class="px-4 md:px-0 flex flex-col justify-end"> <div class="px-4 md:px-0 flex flex-col justify-end">
<h1 class="prose-2xl font-bold self-center my-4" data-testid="nc-survey-form__heading"> <h1 class="text-2xl font-bold text-gray-900 self-center my-4" data-testid="nc-survey-form__heading">
{{ sharedFormView.heading }} {{ sharedFormView.heading }}
</h1> </h1>
<h2 <h2
v-if="sharedFormView.subheading && sharedFormView.subheading !== ''" v-if="sharedFormView.subheading && sharedFormView.subheading !== ''"
class="prose-lg text-slate-500 dark:text-slate-300 self-center mb-4 leading-6" class="text-base font-bold text-gray-500 dark:text-gray-300 self-center mb-4"
data-testid="nc-survey-form__sub-heading" data-testid="nc-survey-form__sub-heading"
> >
{{ sharedFormView?.subheading }} {{ sharedFormView?.subheading }}
@ -264,24 +264,21 @@ onMounted(() => {
class="color-transition h-full flex flex-col mt-6 gap-4 w-full max-w-[max(33%,600px)] m-auto" class="color-transition h-full flex flex-col mt-6 gap-4 w-full max-w-[max(33%,600px)] m-auto"
> >
<div v-if="field && !submitted" class="flex flex-col gap-2"> <div v-if="field && !submitted" class="flex flex-col gap-2">
<div class="flex nc-form-column-label" data-testid="nc-form-column-label"> <div class="nc-form-column-label" data-testid="nc-form-column-label">
<LazySmartsheetHeaderVirtualCell <span>
v-if="isVirtualCol(field)" {{ field.label || field.title }}
:column="{ ...field, title: field.label || field.title }" </span>
:required="isRequired(field, field.required)" <span v-if="isRequired(field, field.required)" class="text-red-500">&nbsp;*</span>
:hide-menu="true" </div>
/> <div
v-if="field?.description"
<LazySmartsheetHeaderCell class="nc-form-column-description text-gray-500 text-sm"
v-else data-testid="nc-survey-form__field-description"
:class="field.uidt === UITypes.Checkbox ? 'nc-form-column-label__checkbox' : ''" >
:column="{ meta: {}, ...field, title: field.label || field.title }" {{ field?.description }}
:required="isRequired(field, field.required)"
:hide-menu="true"
/>
</div> </div>
<LazySmartsheetDivDataCell v-if="field.title" class="relative"> <LazySmartsheetDivDataCell v-if="field.title" class="relative nc-form-data-cell">
<LazySmartsheetVirtualCell <LazySmartsheetVirtualCell
v-if="isVirtualCol(field)" v-if="isVirtualCol(field)"
v-model="formState[field.title]" v-model="formState[field.title]"
@ -304,13 +301,6 @@ onMounted(() => {
<div v-for="error of v$.localState[field.title]?.$errors" :key="error" class="text-red-500"> <div v-for="error of v$.localState[field.title]?.$errors" :key="error" class="text-red-500">
{{ error.$message }} {{ error.$message }}
</div> </div>
<div
class="block text-[14px]"
:class="field.uidt === UITypes.Checkbox ? 'text-center' : ''"
data-testid="nc-survey-form__field-description"
>
{{ field.description }}
</div>
<div v-if="field.uidt === UITypes.LongText" class="text-sm text-gray-500 flex flex-wrap items-center"> <div v-if="field.uidt === UITypes.LongText" class="text-sm text-gray-500 flex flex-wrap items-center">
{{ $t('general.shift') }} <MdiAppleKeyboardShift class="mx-1 text-primary" /> + {{ $t('general.enter') }} {{ $t('general.shift') }} <MdiAppleKeyboardShift class="mx-1 text-primary" /> + {{ $t('general.enter') }}
@ -323,19 +313,18 @@ onMounted(() => {
<div class="ml-1 mt-4 flex w-full text-lg"> <div class="ml-1 mt-4 flex w-full text-lg">
<div class="flex-1 flex justify-center"> <div class="flex-1 flex justify-center">
<div v-if="isLast && !submitted && !v$.$invalid" class="text-center my-4"> <div v-if="isLast && !submitted && !v$.$invalid" class="text-center my-4">
<button <NcButton
:class=" :class="
animationTarget === AnimationTarget.SubmitButton && isAnimating animationTarget === AnimationTarget.SubmitButton && isAnimating
? 'transform translate-y-[1px] translate-x-[1px] ring ring-accent ring-opacity-100' ? 'transform translate-y-[1px] translate-x-[1px] ring ring-accent ring-opacity-100'
: '' : ''
" "
type="submit" html-type="submit"
class="uppercase scaling-btn prose-sm"
data-testid="nc-survey-form__btn-submit" data-testid="nc-survey-form__btn-submit"
@click="submit" @click="submit"
> >
{{ $t('general.submit') }} {{ $t('general.submit') }}
</button> </NcButton>
</div> </div>
<div v-else-if="!submitted" class="flex items-center gap-3 flex-col"> <div v-else-if="!submitted" class="flex items-center gap-3 flex-col">
@ -357,18 +346,20 @@ onMounted(() => {
]" ]"
@click="goNext()" @click="goNext()"
> >
<Transition name="fade"> <div class="flex items-center gap-1">
<span v-if="!v$.localState[field.title]?.$error" class="uppercase text-white">{{ $t('general.ok') }}</span> <Transition name="fade">
</Transition> <span v-if="!v$.localState[field.title]?.$error" class="uppercase text-white">Ok</span>
</Transition>
<Transition name="slide-right" mode="out-in">
<component <Transition name="slide-right" mode="out-in">
:is="iconMap.closeCircle" <component
v-if="v$.localState[field.title]?.$error || columnValidationError" :is="iconMap.closeCircle"
class="text-red-500 md:text-md" v-if="v$.localState[field.title]?.$error || columnValidationError"
/> class="text-red-500 md:text-md"
<component :is="iconMap.check" v-else class="text-white md:text-md" /> />
</Transition> <component :is="iconMap.check" v-else class="text-white md:text-md" />
</Transition>
</div>
</NcButton> </NcButton>
</a-tooltip> </a-tooltip>
@ -381,19 +372,14 @@ onMounted(() => {
<Transition name="slide-left"> <Transition name="slide-left">
<div v-if="submitted" class="flex flex-col justify-center items-center text-center"> <div v-if="submitted" class="flex flex-col justify-center items-center text-center">
<div class="text-lg px-6 py-3 bg-green-300 text-gray-700 rounded" data-testid="nc-survey-form__success-msg"> <a-alert
<template v-if="sharedFormView?.success_msg"> type="success"
{{ sharedFormView?.success_msg }} class="!my-4 !py-4 text-center !rounded-lg"
</template> data-testid="nc-survey-form__success-msg"
outlined
<template v-else> :message="sharedFormView?.success_msg || $t('msg.info.thankYou')"
<div class="flex flex-col gap-1"> :description="sharedFormView?.success_msg ? undefined : $t('msg.info.submittedFormData')"
<div>{{ $t('msg.info.thankYou') }}</div> />
<div>{{ $t('msg.info.submittedFormData') }}</div>
</div>
</template>
</div>
<div v-if="sharedFormView" class="mt-3"> <div v-if="sharedFormView" class="mt-3">
<p v-if="sharedFormView?.show_blank_form" class="text-xs text-slate-500 dark:text-slate-300 text-center my-4"> <p v-if="sharedFormView?.show_blank_form" class="text-xs text-slate-500 dark:text-slate-300 text-center my-4">
@ -401,7 +387,7 @@ onMounted(() => {
</p> </p>
<div v-if="sharedFormView?.submit_another_form" class="text-center"> <div v-if="sharedFormView?.submit_another_form" class="text-center">
<NcButton type="primary" data-testid="nc-survey-form__btn-submit-another-form" @click="resetForm"> <NcButton type="primary" size="medium" data-testid="nc-survey-form__btn-submit-another-form" @click="resetForm">
{{ $t('activity.submitAnotherForm') }} {{ $t('activity.submitAnotherForm') }}
</NcButton> </NcButton>
</div> </div>
@ -468,7 +454,9 @@ onMounted(() => {
</div> </div>
</Transition> </Transition>
<GeneralPoweredBy /> <div v-if="!parseProp(sharedFormView?.meta)?.hide_branding" class="w-full flex justify-center">
<GeneralFormBranding class="inline-flex mx-auto" />
</div>
</div> </div>
</div> </div>
</template> </template>
@ -494,16 +482,37 @@ onMounted(() => {
} }
.nc-input { .nc-input {
@apply appearance-none w-full rounded px-2 py-2 my-2 border-solid border-1 border-primary border-opacity-50; @apply appearance-none w-full !bg-white !rounded-lg border-solid border-1 border-gray-200 focus-within:border-brand-500;
&.nc-cell-rating,
&.nc-cell-geodata {
@apply !py-1;
}
:deep(input) {
@apply !px-1;
}
&.nc-cell-longtext {
@apply p-0 h-auto overflow-hidden;
}
&:not(.nc-cell-longtext) {
@apply px-2 py-2;
:deep(textarea) {
@apply !p-2;
}
}
&.nc-cell-checkbox { &.nc-cell-checkbox {
> * { > * {
@apply justify-center flex items-center; @apply justify-center flex items-center;
} }
} }
}
.nc-form-data-cell.nc-data-cell {
@apply !border-none rounded-none;
input { &:focus-within {
@apply !py-1 !px-1; @apply !border-none;
} }
} }
} }

Loading…
Cancel
Save