Browse Source

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

pull/7664/head
Ramesh Mane 5 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">
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 { setTheme } = useTheme()
const route = useRoute()
const router = useRouter()
watch(
() => sharedViewMeta.value.withTheme,
(hasTheme) => {
if (hasTheme && sharedViewMeta.value.theme) setTheme(sharedViewMeta.value.theme)
},
{ immediate: true },
)
// For now dark theme is disabled
// const onClick = () => {
// isDark.value = !isDark.value
// }
const onClick = () => {
isDark.value = !isDark.value
}
onMounted(() => {
isDark.value = false
})
const shouldRedirect = (to: string) => {
if (sharedViewMeta.value.surveyMode) {
@ -38,11 +33,14 @@ router.afterEach((to) => shouldRedirect(to.name as string))
<template>
<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 />
<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)"
@click="onClick"
>
@ -50,7 +48,7 @@ router.afterEach((to) => shouldRedirect(to.name as string))
<MaterialSymbolsDarkModeOutline v-if="isDark" />
<MaterialSymbolsLightModeOutline v-else />
</Transition>
</div>
</div> -->
</div>
</template>
@ -68,10 +66,6 @@ p {
}
.nc-form-view {
.nc-data-cell {
@apply border-solid border-1 !border-gray-300 dark:!border-slate-200;
}
.nc-cell {
@apply bg-white dark:bg-slate-500;
@ -95,15 +89,7 @@ p {
@apply bg-white dark:bg-slate-500;
&.nc-input {
@apply w-full px-3 min-h-[40px] flex items-center;
&.nc-cell-longtext {
@apply !px-1;
}
&.nc-cell-json {
@apply !h-auto;
}
@apply w-full rounded p-2 min-h-[40px] flex items-center;
.duration-cell-wrapper {
@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 {
@apply px-4 py-2 rounded;
&:focus {
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 { ref } from 'vue'
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 } =
useSharedFormStoreOrThrow()
const {
sharedFormView,
submitForm,
clearForm,
v$,
formState,
notFound,
formColumns,
submitted,
secondsRemain,
isLoading,
progress,
} = useSharedFormStoreOrThrow()
function isRequired(_columnObj: Record<string, any>, required = false) {
let columnObj = _columnObj
@ -21,8 +32,6 @@ function isRequired(_columnObj: Record<string, any>, required = false) {
return !!(required || (columnObj && columnObj.rqd && !columnObj.cdf))
}
const { isMobileMode } = useGlobal()
const fieldTitleForCurrentScan = ref('')
const scannerIsReady = ref(false)
@ -70,32 +79,37 @@ const onDecode = async (scannedCodeValue: string) => {
</script>
<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
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">
<h1 class="prose-2xl font-bold self-center my-4 break-words">
{{ sharedFormView.heading }}
</h1>
<div class="mb-4">
<h1 class="text-2xl font-bold text-gray-900 mb-4">
{{ sharedFormView.heading }}
</h1>
<h2
v-if="sharedFormView.subheading"
class="prose-lg text-slate-500 dark:text-slate-300 self-center mb-4 leading-6 break-words"
>
{{ sharedFormView.subheading }}
</h2>
<h2 v-if="sharedFormView.subheading" class="text-base font-bold text-gray-500 dark:text-slate-300 mb-4">
{{ sharedFormView.subheading }}
</h2>
</div>
<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">
<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
type="success"
class="my-4 text-center"
class="!my-4 text-center !rounded-lg"
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">
@ -103,7 +117,9 @@ const onDecode = async (scannedCodeValue: string) => {
</p>
<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>
@ -124,7 +140,7 @@ const onDecode = async (scannedCodeValue: string) => {
<StreamBarcodeReader v-show="scannerIsReady" @decode="onDecode" @loaded="onLoaded"> </StreamBarcodeReader>
</div>
</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">
<a-spin size="large" />
</div>
@ -132,22 +148,16 @@ const onDecode = async (scannedCodeValue: string) => {
<div class="nc-form-wrapper">
<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 class="flex nc-form-column-label">
<LazySmartsheetHeaderVirtualCell
v-if="isVirtualCol(field)"
:column="{ ...field, title: field.label || field.title }"
:required="isRequired(field, field.required)"
:hide-menu="true"
/>
<LazySmartsheetHeaderCell
v-else
:column="{ ...field, title: field.label || field.title }"
:required="isRequired(field, field.required)"
:hide-menu="true"
/>
<div class="nc-form-column-label">
<span>
{{ field.label || field.title }}
</span>
<span v-if="isRequired(field, field.required)" class="text-red-500">&nbsp;*</span>
</div>
<div v-if="field?.description" class="nc-form-column-description text-gray-500 text-sm">
{{ field?.description }}
</div>
<div>
@ -182,34 +192,59 @@ const onDecode = async (scannedCodeValue: string) => {
</a-button>
</LazySmartsheetDivDataCell>
<div
class="flex flex-col gap-2 text-slate-500 dark:text-slate-300 text-[0.75rem] my-2 px-1 leading-[18px]"
style="word-break: break-word"
>
<div v-for="error of v$.localState[field.title]?.$errors" :key="error" class="text-red-500">
{{ error.$message }}
</div>
{{ field.description }}
<div class="flex flex-col gap-2 text-slate-500 dark:text-slate-300 text-sm mt-2">
<template v-if="isVirtualCol(field)">
<div v-for="error of v$.virtual[field.title]?.$errors" :key="`${error}virtual`" class="text-red-500">
{{ error.$message }}
</div>
</template>
<template v-else>
<div v-for="error of v$.localState[field.title]?.$errors" :key="error" class="text-red-500">
{{ error.$message }}
</div>
</template>
</div>
</div>
</div>
</div>
<div class="text-center mt-4">
<NcButton type="primary" html-type="submit" data-testid="shared-form-submit-button" @click="submitForm">
<div class="flex justify-between items-center mt-6">
<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') }}
</NcButton>
</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>
</div>
<div class="flex items-end">
<GeneralPoweredBy />
</div>
<div>&nbsp;</div>
</div>
</template>
@ -221,4 +256,10 @@ const onDecode = async (scannedCodeValue: string) => {
@apply h-auto;
@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>

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"
>
<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 }}
</h1>
<h2
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"
>
{{ 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"
>
<div v-if="field && !submitted" class="flex flex-col gap-2">
<div class="flex nc-form-column-label" data-testid="nc-form-column-label">
<LazySmartsheetHeaderVirtualCell
v-if="isVirtualCol(field)"
:column="{ ...field, title: field.label || field.title }"
:required="isRequired(field, field.required)"
:hide-menu="true"
/>
<LazySmartsheetHeaderCell
v-else
:class="field.uidt === UITypes.Checkbox ? 'nc-form-column-label__checkbox' : ''"
:column="{ meta: {}, ...field, title: field.label || field.title }"
:required="isRequired(field, field.required)"
:hide-menu="true"
/>
<div class="nc-form-column-label" data-testid="nc-form-column-label">
<span>
{{ field.label || field.title }}
</span>
<span v-if="isRequired(field, field.required)" class="text-red-500">&nbsp;*</span>
</div>
<div
v-if="field?.description"
class="nc-form-column-description text-gray-500 text-sm"
data-testid="nc-survey-form__field-description"
>
{{ field?.description }}
</div>
<LazySmartsheetDivDataCell v-if="field.title" class="relative">
<LazySmartsheetDivDataCell v-if="field.title" class="relative nc-form-data-cell">
<LazySmartsheetVirtualCell
v-if="isVirtualCol(field)"
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">
{{ error.$message }}
</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">
{{ $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="flex-1 flex justify-center">
<div v-if="isLast && !submitted && !v$.$invalid" class="text-center my-4">
<button
<NcButton
:class="
animationTarget === AnimationTarget.SubmitButton && isAnimating
? 'transform translate-y-[1px] translate-x-[1px] ring ring-accent ring-opacity-100'
: ''
"
type="submit"
class="uppercase scaling-btn prose-sm"
html-type="submit"
data-testid="nc-survey-form__btn-submit"
@click="submit"
>
{{ $t('general.submit') }}
</button>
</NcButton>
</div>
<div v-else-if="!submitted" class="flex items-center gap-3 flex-col">
@ -357,18 +346,20 @@ onMounted(() => {
]"
@click="goNext()"
>
<Transition name="fade">
<span v-if="!v$.localState[field.title]?.$error" class="uppercase text-white">{{ $t('general.ok') }}</span>
</Transition>
<Transition name="slide-right" mode="out-in">
<component
:is="iconMap.closeCircle"
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>
<div class="flex items-center gap-1">
<Transition name="fade">
<span v-if="!v$.localState[field.title]?.$error" class="uppercase text-white">Ok</span>
</Transition>
<Transition name="slide-right" mode="out-in">
<component
:is="iconMap.closeCircle"
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>
</div>
</NcButton>
</a-tooltip>
@ -381,19 +372,14 @@ onMounted(() => {
<Transition name="slide-left">
<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">
<template v-if="sharedFormView?.success_msg">
{{ sharedFormView?.success_msg }}
</template>
<template v-else>
<div class="flex flex-col gap-1">
<div>{{ $t('msg.info.thankYou') }}</div>
<div>{{ $t('msg.info.submittedFormData') }}</div>
</div>
</template>
</div>
<a-alert
type="success"
class="!my-4 !py-4 text-center !rounded-lg"
data-testid="nc-survey-form__success-msg"
outlined
:message="sharedFormView?.success_msg || $t('msg.info.thankYou')"
:description="sharedFormView?.success_msg ? undefined : $t('msg.info.submittedFormData')"
/>
<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">
@ -401,7 +387,7 @@ onMounted(() => {
</p>
<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') }}
</NcButton>
</div>
@ -468,7 +454,9 @@ onMounted(() => {
</div>
</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>
</template>
@ -494,16 +482,37 @@ onMounted(() => {
}
.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 {
> * {
@apply justify-center flex items-center;
}
}
}
.nc-form-data-cell.nc-data-cell {
@apply !border-none rounded-none;
input {
@apply !py-1 !px-1;
&:focus-within {
@apply !border-none;
}
}
}

Loading…
Cancel
Save