|
|
|
@ -1,5 +1,7 @@
|
|
|
|
|
<script setup lang="ts"> |
|
|
|
|
import Draggable from 'vuedraggable' |
|
|
|
|
import { Pane, Splitpanes } from 'splitpanes' |
|
|
|
|
import 'splitpanes/dist/splitpanes.css' |
|
|
|
|
import { RelationTypes, UITypes, ViewTypes, getSystemColumns, isLinksOrLTAR, isVirtualCol } from 'nocodb-sdk' |
|
|
|
|
import type { Permission } from '#imports' |
|
|
|
|
import { |
|
|
|
@ -113,6 +115,8 @@ const activeRow = ref('')
|
|
|
|
|
|
|
|
|
|
const editEnabled = ref<boolean[]>([]) |
|
|
|
|
|
|
|
|
|
const searchQuery = ref('') |
|
|
|
|
|
|
|
|
|
const { t } = useI18n() |
|
|
|
|
|
|
|
|
|
const { betaFeatureToggleState } = useBetaFeatureToggle() |
|
|
|
@ -402,6 +406,7 @@ const onFormItemClick = (element: any) => {
|
|
|
|
|
</script> |
|
|
|
|
|
|
|
|
|
<template> |
|
|
|
|
<div class="h-full"> |
|
|
|
|
<template v-if="isMobileMode"> |
|
|
|
|
<div class="pl-6 pr-[120px] py-6 bg-white flex-col justify-start items-start gap-2.5 inline-flex"> |
|
|
|
|
<div class="text-gray-500 text-5xl font-semibold leading-16"> |
|
|
|
@ -440,130 +445,17 @@ const onFormItemClick = (element: any) => {
|
|
|
|
|
</a-row> |
|
|
|
|
|
|
|
|
|
<a-row v-else class="h-full flex" data-testid="nc-form-wrapper"> |
|
|
|
|
<a-col v-if="isEditable" :span="8" class="shadow p-2 md:p-4 h-full overflow-auto scrollbar-thin-dull nc-form-left-drawer"> |
|
|
|
|
<div class="flex flex-wrap gap-2"> |
|
|
|
|
<div class="flex-1 text-lg"> |
|
|
|
|
{{ $t('objects.fields') }} |
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
<div class="flex flex-wrap gap-2 mb-4"> |
|
|
|
|
<NcButton |
|
|
|
|
v-if="hiddenColumns.length" |
|
|
|
|
type="secondary" |
|
|
|
|
class="nc-form-add-all" |
|
|
|
|
data-testid="nc-form-add-all" |
|
|
|
|
:disabled="isLocked" |
|
|
|
|
@click="addAllColumns" |
|
|
|
|
> |
|
|
|
|
<!-- Add all --> |
|
|
|
|
{{ $t('general.addAll') }} |
|
|
|
|
</NcButton> |
|
|
|
|
|
|
|
|
|
<NcButton |
|
|
|
|
v-if="localColumns.length" |
|
|
|
|
type="secondary" |
|
|
|
|
class="nc-form-remove-all" |
|
|
|
|
data-testid="nc-form-remove-all" |
|
|
|
|
:disabled="isLocked" |
|
|
|
|
@click="removeAllColumns" |
|
|
|
|
> |
|
|
|
|
<!-- Remove all --> |
|
|
|
|
{{ $t('general.removeAll') }} |
|
|
|
|
</NcButton> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
<Draggable |
|
|
|
|
:list="hiddenColumns" |
|
|
|
|
item-key="id" |
|
|
|
|
draggable=".item" |
|
|
|
|
group="form-inputs" |
|
|
|
|
:disabled="isLocked" |
|
|
|
|
class="flex flex-col gap-2" |
|
|
|
|
@start="drag = true" |
|
|
|
|
@end="drag = false" |
|
|
|
|
> |
|
|
|
|
<template #item="{ element, index }"> |
|
|
|
|
<a-card |
|
|
|
|
size="small" |
|
|
|
|
class="!border-0 color-transition cursor-pointer item hover:(bg-primary ring-1 ring-accent ring-opacity-100) bg-opacity-10 !rounded !shadow-lg" |
|
|
|
|
:class="{ |
|
|
|
|
'!bg-gray-50 !hover:bg-gray-50 !hover:ring-transparent !cursor-not-allowed': isLocked, |
|
|
|
|
}" |
|
|
|
|
:data-testid="`nc-form-hidden-column-${element.label || element.title}`" |
|
|
|
|
@mousedown="moved = false" |
|
|
|
|
@mousemove="moved = false" |
|
|
|
|
@mouseup="handleMouseUp(element, index)" |
|
|
|
|
> |
|
|
|
|
<div class="flex"> |
|
|
|
|
<div class="flex flex-1"> |
|
|
|
|
<LazySmartsheetHeaderVirtualCell |
|
|
|
|
v-if="isVirtualCol(element)" |
|
|
|
|
:column="{ ...element, title: element.label || element.title }" |
|
|
|
|
:required="isRequired(element, element.required)" |
|
|
|
|
:hide-menu="true" |
|
|
|
|
/> |
|
|
|
|
|
|
|
|
|
<LazySmartsheetHeaderCell |
|
|
|
|
v-else |
|
|
|
|
:column="{ ...element, title: element.label || element.title }" |
|
|
|
|
:required="isRequired(element, element.required)" |
|
|
|
|
:hide-menu="true" |
|
|
|
|
/> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
</a-card> |
|
|
|
|
</template> |
|
|
|
|
|
|
|
|
|
<template v-if="!isLocked" #footer> |
|
|
|
|
<div |
|
|
|
|
class="my-4 select-none border-dashed border-2 border-gray-400 py-3 text-gray-400 text-center nc-drag-n-drop-to-hide" |
|
|
|
|
data-testid="nc-drag-n-drop-to-hide" |
|
|
|
|
> |
|
|
|
|
<!-- Drag and drop fields here to hide --> |
|
|
|
|
{{ $t('msg.info.dragDropHide') }} |
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
<a-dropdown |
|
|
|
|
v-model:visible="showColumnDropdown" |
|
|
|
|
:trigger="['click']" |
|
|
|
|
overlay-class-name="nc-dropdown-form-add-column" |
|
|
|
|
> |
|
|
|
|
<button type="button" class="group w-full mt-2" @click.stop="showColumnDropdown = true"> |
|
|
|
|
<span class="flex items-center flex-wrap justify-center gap-2 prose-sm text-gray-400"> |
|
|
|
|
<component :is="iconMap.plus" class="color-transition transform group-hover:(text-accent scale-125)" /> |
|
|
|
|
<!-- Add new field to this table --> |
|
|
|
|
<span class="color-transition group-hover:text-primary break-words"> |
|
|
|
|
{{ $t('activity.addField') }} |
|
|
|
|
</span> |
|
|
|
|
</span> |
|
|
|
|
</button> |
|
|
|
|
|
|
|
|
|
<template #overlay> |
|
|
|
|
<SmartsheetColumnEditOrAddProvider |
|
|
|
|
v-if="showColumnDropdown" |
|
|
|
|
@submit="submitCallback" |
|
|
|
|
@cancel="showColumnDropdown = false" |
|
|
|
|
@click.stop |
|
|
|
|
@keydown.stop |
|
|
|
|
/> |
|
|
|
|
</template> |
|
|
|
|
</a-dropdown> |
|
|
|
|
</template> |
|
|
|
|
</Draggable> |
|
|
|
|
</a-col> |
|
|
|
|
|
|
|
|
|
<a-col v-if="formViewData" :span="isEditable ? 16 : 24" class="h-full overflow-auto scrollbar-thin-dull"> |
|
|
|
|
<div class="h-[200px] bg-primary bg-opacity-75"> |
|
|
|
|
<a-col v-if="formViewData" :span="isEditable ? 16 : 24" class="h-full overflow-auto scrollbar-thin-dull p-6 bg-gray-50"> |
|
|
|
|
<div class="h-[160px] bg-primary bg-opacity-75 border-gray-200 rounded-3xl"> |
|
|
|
|
<!-- for future implementation of cover image --> |
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
<a-card |
|
|
|
|
class="p-4 border-none" |
|
|
|
|
class="p-4 border-none !mt-6 max-w-[688px] !mx-auto" |
|
|
|
|
:body-style="{ |
|
|
|
|
maxWidth: 'max(50vw, 700px)', |
|
|
|
|
margin: '0 auto', |
|
|
|
|
marginTop: '-200px', |
|
|
|
|
|
|
|
|
|
padding: '0px', |
|
|
|
|
}" |
|
|
|
|
> |
|
|
|
@ -836,30 +728,194 @@ const onFormItemClick = (element: any) => {
|
|
|
|
|
</a-form> |
|
|
|
|
|
|
|
|
|
<a-divider v-if="!isLocked" /> |
|
|
|
|
</a-card> |
|
|
|
|
</a-col> |
|
|
|
|
<a-col v-if="isEditable" :span="8" class="h-full nc-form-left-drawer border-l border-gray-200"> |
|
|
|
|
<Splitpanes horizontal class="w-full nc-form-right-splitpane"> |
|
|
|
|
<Pane min-size="40%" size="50" class="nc-form-right-splitpane-item p-2 md:p-4 flex flex-col space-y-4"> |
|
|
|
|
<div class="flex flex-wrap justify-between items-center gap-2"> |
|
|
|
|
<div class="flex gap-3"> |
|
|
|
|
<div class="text-base font-bold text-gray-900"> |
|
|
|
|
<!-- {{ $t('objects.fields') }} --> |
|
|
|
|
Form Fields |
|
|
|
|
</div> |
|
|
|
|
<NcBadge color="border-gray-200" class=""> 22/26 Field </NcBadge> |
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
<div v-if="isEditable && !isLocked" class="px-4 flex flex-col gap-2"> |
|
|
|
|
<a-dropdown |
|
|
|
|
v-model:visible="showColumnDropdown" |
|
|
|
|
:trigger="['click']" |
|
|
|
|
overlay-class-name="nc-dropdown-form-add-column" |
|
|
|
|
> |
|
|
|
|
<NcButton |
|
|
|
|
v-if="hiddenColumns.length" |
|
|
|
|
type="secondary" |
|
|
|
|
size="small" |
|
|
|
|
class="nc-form-add-field" |
|
|
|
|
data-testid="nc-form-add-field" |
|
|
|
|
@click.stop="showColumnDropdown = true" |
|
|
|
|
> |
|
|
|
|
<div class="flex gap-2 items-center"> |
|
|
|
|
<component :is="iconMap.plus" class="color-transition transform group-hover:(text-accent scale-125)" /> |
|
|
|
|
<span> Add Field </span> |
|
|
|
|
</div> |
|
|
|
|
</NcButton> |
|
|
|
|
|
|
|
|
|
<template #overlay> |
|
|
|
|
<SmartsheetColumnEditOrAddProvider |
|
|
|
|
v-if="showColumnDropdown" |
|
|
|
|
@submit="submitCallback" |
|
|
|
|
@cancel="showColumnDropdown = false" |
|
|
|
|
@click.stop |
|
|
|
|
@keydown.stop |
|
|
|
|
/> |
|
|
|
|
</template> |
|
|
|
|
</a-dropdown> |
|
|
|
|
<!-- <NcButton |
|
|
|
|
v-if="hiddenColumns.length" |
|
|
|
|
type="secondary" |
|
|
|
|
class="nc-form-add-all" |
|
|
|
|
data-testid="nc-form-add-all" |
|
|
|
|
:disabled="isLocked" |
|
|
|
|
@click="addAllColumns" |
|
|
|
|
> |
|
|
|
|
Add all |
|
|
|
|
{{ $t('general.addAll') }} |
|
|
|
|
</NcButton> |
|
|
|
|
|
|
|
|
|
<NcButton |
|
|
|
|
v-if="localColumns.length" |
|
|
|
|
type="secondary" |
|
|
|
|
class="nc-form-remove-all" |
|
|
|
|
data-testid="nc-form-remove-all" |
|
|
|
|
:disabled="isLocked" |
|
|
|
|
@click="removeAllColumns" |
|
|
|
|
> |
|
|
|
|
Remove all |
|
|
|
|
{{ $t('general.removeAll') }} |
|
|
|
|
</NcButton> --> |
|
|
|
|
</div> |
|
|
|
|
<div> |
|
|
|
|
<a-input |
|
|
|
|
v-model:value="searchQuery" |
|
|
|
|
data-testid="nc-form-field-search-input" |
|
|
|
|
class="!h-9 !px-3 !py-1 !rounded-lg" |
|
|
|
|
:placeholder="$t('placeholder.searchFields') + '...'" |
|
|
|
|
> |
|
|
|
|
<template #prefix> |
|
|
|
|
<GeneralIcon icon="search" class="mr-2 h-4 w-4 text-gray-500 group-hover:text-black" /> |
|
|
|
|
</template> |
|
|
|
|
<template #suffix> |
|
|
|
|
<GeneralIcon |
|
|
|
|
v-if="searchQuery.length > 0" |
|
|
|
|
icon="close" |
|
|
|
|
class="ml-2 h-4 w-4 text-gray-500 group-hover:text-black" |
|
|
|
|
data-testid="nc-form-field-clear-search" |
|
|
|
|
@click="searchQuery = ''" |
|
|
|
|
/> |
|
|
|
|
</template> |
|
|
|
|
</a-input> |
|
|
|
|
</div> |
|
|
|
|
<div class="nc-form-fields-list border-1 border-gray-200 rounded-lg overflow-y-auto"> |
|
|
|
|
<div |
|
|
|
|
v-if="!hiddenColumns?.filter((el) => el.title.toLowerCase().includes(searchQuery.toLowerCase())).length" |
|
|
|
|
class="px-0.5 py-2 text-gray-500 text-center" |
|
|
|
|
> |
|
|
|
|
{{ $t('title.noFieldsFound') }} |
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
<Draggable |
|
|
|
|
v-else |
|
|
|
|
v-model="hiddenColumns" |
|
|
|
|
item-key="id" |
|
|
|
|
@change="onMove($event)" |
|
|
|
|
:style="{ height: 'calc(100% - 64px)' }" |
|
|
|
|
> |
|
|
|
|
<template #item="{ element: field }"> |
|
|
|
|
<div |
|
|
|
|
v-if="field.title.toLowerCase().includes(searchQuery.toLowerCase())" |
|
|
|
|
:key="field.id" |
|
|
|
|
class="p-2 flex flex-row items-center border-b-1 last:border-none first:rounded-t-lg border-gray-200" |
|
|
|
|
:data-testid="`nc-form-field-item-${field.title}`" |
|
|
|
|
@click.stop |
|
|
|
|
> |
|
|
|
|
<component :is="iconMap.drag" class="flex-none cursor-move !h-4 !w-4 text-gray-600 mr-1" /> |
|
|
|
|
<div |
|
|
|
|
class="flex-1 flex items-center justify-between cursor-pointer" |
|
|
|
|
@click=" |
|
|
|
|
() => { |
|
|
|
|
field.show = !field.show |
|
|
|
|
} |
|
|
|
|
" |
|
|
|
|
> |
|
|
|
|
<div class="flex-1 flex items-center"> |
|
|
|
|
<SmartsheetHeaderVirtualCellIcon v-if="field && isVirtualCol(field)" :column-meta="field" /> |
|
|
|
|
<SmartsheetHeaderCellIcon v-else :column-meta="field" /> |
|
|
|
|
|
|
|
|
|
<NcTooltip class="truncate flex-1 ml-1" show-on-truncate-only> |
|
|
|
|
<template #title> {{ field.title }} </template> |
|
|
|
|
<span data-testid="nc-field-title"> |
|
|
|
|
{{ field.title }} |
|
|
|
|
</span> |
|
|
|
|
</NcTooltip> |
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
<NcSwitch :checked="field.show" /> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
</template> |
|
|
|
|
<template #header> |
|
|
|
|
<div |
|
|
|
|
key="nc-form-show-all-fields" |
|
|
|
|
class="w-full px-2 py-2 flex flex-row items-center border-b-1 rounded-t-lg border-gray-200 bg-gray-50 sticky top-0 z-100 overflow-hidden" |
|
|
|
|
data-testid="nc" |
|
|
|
|
@click.stop |
|
|
|
|
> |
|
|
|
|
<div class="w-4 h-4 flex-none mr-2"></div> |
|
|
|
|
|
|
|
|
|
<div class="flex-1 flex flex-row items-center truncate cursor-pointer"> |
|
|
|
|
<div class="flex-1 font-base">Select all fields</div> |
|
|
|
|
<NcSwitch :checked="true" /> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
</template> |
|
|
|
|
</Draggable> |
|
|
|
|
</div> |
|
|
|
|
</Pane> |
|
|
|
|
<Pane |
|
|
|
|
v-if="isEditable && !isLocked && formViewData" |
|
|
|
|
min-size="20%" |
|
|
|
|
size="35" |
|
|
|
|
class="nc-form-right-splitpane-item p-2 md:p-4 flex flex-col space-y-4 !overflow-y-auto nc-scrollbar-x-lg" |
|
|
|
|
> |
|
|
|
|
<!-- After form is submitted --> |
|
|
|
|
<div class="text-lg text-gray-700"> |
|
|
|
|
{{ $t('msg.info.afterFormSubmitted') }} |
|
|
|
|
<div class="text-base font-bold text-gray-900"> |
|
|
|
|
Post Form Submission Settings |
|
|
|
|
<!-- {{ $t('msg.info.afterFormSubmitted') }} --> |
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
<!-- Show this message --> |
|
|
|
|
<div class="text-gray-500 text-bold">{{ $t('msg.info.showMessage') }}:</div> |
|
|
|
|
<div> |
|
|
|
|
<div class="text-gray-800 mb-2"> |
|
|
|
|
Display Message |
|
|
|
|
<!-- {{ $t('msg.info.showMessage') }} --> |
|
|
|
|
</div> |
|
|
|
|
<a-textarea |
|
|
|
|
v-model:value="formViewData.success_msg" |
|
|
|
|
v-model:value="formViewData.success_msg as string" |
|
|
|
|
:rows="3" |
|
|
|
|
hide-details |
|
|
|
|
class="nc-form-after-submit-msg" |
|
|
|
|
class="nc-form-after-submit-msg !rounded-lg !px-3 !py-1" |
|
|
|
|
data-testid="nc-form-after-submit-msg" |
|
|
|
|
@change="updateView" |
|
|
|
|
/> |
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
<!-- Other options --> |
|
|
|
|
<div class="flex flex-col gap-2 mt-4"> |
|
|
|
|
<div class="flex flex-col gap-3"> |
|
|
|
|
<div class="flex items-center"> |
|
|
|
|
<!-- Show "Submit Another Form" button --> |
|
|
|
|
<a-switch |
|
|
|
|
v-model:checked="formViewData.submit_another_form" |
|
|
|
|
v-model:checked="formViewData.submit_another_form as boolean" |
|
|
|
|
v-e="[`a:form-view:submit-another-form`]" |
|
|
|
|
size="small" |
|
|
|
|
class="nc-form-checkbox-submit-another-form" |
|
|
|
@ -872,7 +928,7 @@ const onFormItemClick = (element: any) => {
|
|
|
|
|
<div class="flex items-center"> |
|
|
|
|
<!-- Show a blank form after 5 seconds --> |
|
|
|
|
<a-switch |
|
|
|
|
v-model:checked="formViewData.show_blank_form" |
|
|
|
|
v-model:checked="formViewData.show_blank_form as boolean" |
|
|
|
|
v-e="[`a:form-view:show-blank-form`]" |
|
|
|
|
size="small" |
|
|
|
|
class="nc-form-checkbox-show-blank-form" |
|
|
|
@ -900,11 +956,64 @@ const onFormItemClick = (element: any) => {
|
|
|
|
|
</span> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
</Pane> |
|
|
|
|
<!-- <Draggable |
|
|
|
|
:list="hiddenColumns" |
|
|
|
|
item-key="id" |
|
|
|
|
draggable=".item" |
|
|
|
|
group="form-inputs" |
|
|
|
|
:disabled="isLocked" |
|
|
|
|
class="flex flex-col gap-2" |
|
|
|
|
@start="drag = true" |
|
|
|
|
@end="drag = false" |
|
|
|
|
> |
|
|
|
|
<template #item="{ element, index }"> |
|
|
|
|
<a-card |
|
|
|
|
size="small" |
|
|
|
|
class="!border-0 color-transition cursor-pointer item hover:(bg-primary ring-1 ring-accent ring-opacity-100) bg-opacity-10 !rounded !shadow-lg" |
|
|
|
|
:class="{ |
|
|
|
|
'!bg-gray-50 !hover:bg-gray-50 !hover:ring-transparent !cursor-not-allowed': isLocked, |
|
|
|
|
}" |
|
|
|
|
:data-testid="`nc-form-hidden-column-${element.label || element.title}`" |
|
|
|
|
@mousedown="moved = false" |
|
|
|
|
@mousemove="moved = false" |
|
|
|
|
@mouseup="handleMouseUp(element, index)" |
|
|
|
|
> |
|
|
|
|
<div class="flex"> |
|
|
|
|
<div class="flex flex-1"> |
|
|
|
|
<LazySmartsheetHeaderVirtualCell |
|
|
|
|
v-if="isVirtualCol(element)" |
|
|
|
|
:column="{ ...element, title: element.label || element.title }" |
|
|
|
|
:required="isRequired(element, element.required)" |
|
|
|
|
:hide-menu="true" |
|
|
|
|
/> |
|
|
|
|
|
|
|
|
|
<LazySmartsheetHeaderCell |
|
|
|
|
v-else |
|
|
|
|
:column="{ ...element, title: element.label || element.title }" |
|
|
|
|
:required="isRequired(element, element.required)" |
|
|
|
|
:hide-menu="true" |
|
|
|
|
/> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
</a-card> |
|
|
|
|
</a-col> |
|
|
|
|
</template> |
|
|
|
|
|
|
|
|
|
<template v-if="!isLocked" #footer> |
|
|
|
|
<div |
|
|
|
|
class="my-4 select-none border-dashed border-2 border-gray-400 py-3 text-gray-400 text-center nc-drag-n-drop-to-hide" |
|
|
|
|
data-testid="nc-drag-n-drop-to-hide" |
|
|
|
|
> |
|
|
|
|
|
|
|
|
|
{{ $t('msg.info.dragDropHide') }} |
|
|
|
|
</div> |
|
|
|
|
</template> |
|
|
|
|
</Draggable> --> |
|
|
|
|
</Splitpanes></a-col |
|
|
|
|
> |
|
|
|
|
</a-row> |
|
|
|
|
</template> |
|
|
|
|
</div> |
|
|
|
|
</template> |
|
|
|
|
|
|
|
|
|
<style scoped lang="scss"> |
|
|
|
@ -967,4 +1076,14 @@ const onFormItemClick = (element: any) => {
|
|
|
|
|
@apply !border-none; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
:deep(.nc-form-right-splitpane .splitpanes__splitter) { |
|
|
|
|
@apply !border-b-1 !border-gray-200; |
|
|
|
|
|
|
|
|
|
::after { |
|
|
|
|
content: 'e945'; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
.nc-form-fields-list { |
|
|
|
|
} |
|
|
|
|
</style> |
|
|
|
|