mirror of https://github.com/nocodb/nocodb
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
250 lines
7.3 KiB
250 lines
7.3 KiB
<script setup lang="ts"> |
|
import type { VNodeRef } from '@vue/runtime-core' |
|
import type { ViewType } from 'nocodb-sdk' |
|
import { viewTypeAlias } from 'nocodb-sdk' |
|
import { LockType } from '#imports' |
|
|
|
const props = defineProps<{ |
|
modelValue?: boolean |
|
view?: ViewType |
|
changeType?: LockType |
|
}>() |
|
|
|
const emits = defineEmits(['update:modelValue']) |
|
|
|
const dialogShow = useVModel(props, 'modelValue', emits, { defaultValue: false }) |
|
|
|
const isForm = inject(IsFormInj, ref(false)) |
|
|
|
const { $api, $e } = useNuxtApp() |
|
|
|
const { isUIAllowed } = useRoles() |
|
|
|
const { user } = useGlobal() |
|
|
|
const { idUserMap } = storeToRefs(useBase()) |
|
|
|
const { activeView } = storeToRefs(useViewsStore()) |
|
|
|
const view = computed(() => props.view || activeView.value) |
|
|
|
const changeType = computed(() => |
|
view.value?.lock_type !== LockType.Locked ? LockType.Locked : props.changeType || LockType.Collaborative, |
|
) |
|
|
|
const focusInput: VNodeRef = (el) => el && el?.focus?.() |
|
|
|
const formValidator = ref() |
|
|
|
const isErrored = ref(false) |
|
|
|
const form = reactive({ |
|
description: '', |
|
}) |
|
|
|
const formRules = { |
|
description: [{ max: 1000, message: 'Value must be at most 1000 characters long' }], |
|
} |
|
|
|
const isLoading = ref(false) |
|
|
|
const changeLockType = async () => { |
|
if (!view.value) return |
|
|
|
if (changeType.value === LockType.Locked) { |
|
const valid = await formValidator.value.validate() |
|
|
|
if (!valid) return |
|
} |
|
|
|
const payload = { |
|
lockedViewDescription: changeType.value === LockType.Locked ? form.description : '', |
|
lockedByUserId: changeType.value === LockType.Locked ? user.value?.id : '', |
|
} |
|
|
|
isLoading.value = true |
|
|
|
try { |
|
await $api.dbView.update(view.value.id as string, { |
|
lock_type: changeType.value, |
|
meta: { |
|
...parseProp(view.value.meta), |
|
...payload, |
|
}, |
|
}) |
|
|
|
view.value.meta = { |
|
...parseProp(view.value.meta), |
|
...payload, |
|
} |
|
|
|
view.value.lock_type = changeType.value |
|
|
|
message.success(`Successfully Switched to ${view.value.lock_type} view`) |
|
|
|
$e(`a:${viewTypeAlias[view.value.type] || 'view'}:lock`, { lockType: view.value.lock_type, title: view.value.title }) |
|
} catch (e: any) { |
|
message.error(await extractSdkResponseErrorMsg(e)) |
|
} finally { |
|
dialogShow.value = false |
|
isLoading.value = false |
|
} |
|
} |
|
|
|
onKeyStroke('Enter', () => { |
|
if (isLoading.value || !dialogShow.value || isForm.value || !(changeType.value !== LockType.Locked)) return |
|
|
|
changeLockType() |
|
}) |
|
|
|
watch(dialogShow, (newValue) => { |
|
if (!newValue) { |
|
form.description = '' |
|
isErrored.value = false |
|
} |
|
}) |
|
|
|
watch( |
|
() => form.description, |
|
async () => { |
|
if (changeType.value !== LockType.Locked) return |
|
|
|
try { |
|
isErrored.value = !(await formValidator.value.validate()) |
|
} catch (e: any) { |
|
isErrored.value = true |
|
} |
|
}, |
|
) |
|
</script> |
|
|
|
<template> |
|
<div v-if="isForm" class="nc-unlock-view-wrapper rounded-2xl bg-white p-6 w-full max-w-[384px] flex flex-col gap-5"> |
|
<div class="flex flex-col gap-2"> |
|
<div class="text-base font-bold text-nc-content-gray-emphasis"> |
|
{{ $t('title.thisFormIsLocked') }} |
|
</div> |
|
<div v-if="view?.meta?.lockedViewDescription" class="text-sm bg-nc-bg-gray-light rounded-lg px-2 py-2"> |
|
{{ view?.meta?.lockedViewDescription }} |
|
</div> |
|
<div class="text-sm text-nc-content-gray">{{ $t('title.unlockThisVieToMakeChanges') }}</div> |
|
<div class="text-sm text-nc-content-gray"> |
|
{{ $t('title.unlockViewTitleSubtitle') }} |
|
<span v-if="idUserMap[view?.meta?.lockedByUserId]?.id === user.id" class="font-bold"> {{ t('general.you') }} </span> |
|
<span v-else class="font-bold"> |
|
{{ idUserMap[view?.meta?.lockedByUserId]?.display_name || idUserMap[view?.meta?.lockedByUserId]?.email }} |
|
</span> |
|
</div> |
|
</div> |
|
|
|
<NcButton |
|
v-if="isUIAllowed('fieldAdd')" |
|
type="secondary" |
|
size="small" |
|
class="!w-full" |
|
:loading="isLoading" |
|
:disabled="isLoading" |
|
@click="changeLockType" |
|
> |
|
<template #icon> |
|
<GeneralIcon icon="ncUnlock" class="flex-none" /> |
|
</template> |
|
{{ $t('labels.unlockView') }} |
|
</NcButton> |
|
</div> |
|
<NcModal |
|
v-else |
|
v-model:visible="dialogShow" |
|
:show-separator="false" |
|
:header="$t('activity.createTable')" |
|
size="small" |
|
wrap-class-name="nc-lock-view-modal-wrapper" |
|
@keydown.esc="dialogShow = false" |
|
> |
|
<template v-if="changeType === LockType.Locked" #header> |
|
<div class="flex flex-col gap-2 w-full"> |
|
<div class="text-base font-bold text-nc-content-gray-emphasis"> |
|
{{ $t('title.lockThisView') }} |
|
</div> |
|
<div class="text-sm font-normal text-nc-content-gray-subtle"> |
|
{{ $t('title.lockThisViewSubtle') }} |
|
</div> |
|
</div> |
|
</template> |
|
|
|
<a-form |
|
ref="formValidator" |
|
:model="form" |
|
layout="vertical" |
|
name="create-new-table-form" |
|
class="flex flex-col gap-5 !mt-1 font-normal" |
|
@finish="changeLockType" |
|
> |
|
<div v-if="changeType === LockType.Locked" class="flex flex-col gap-5"> |
|
<a-form-item name="description" :rules="formRules.description"> |
|
<a-textarea |
|
:ref="focusInput" |
|
v-model:value="form.description" |
|
class="!rounded-lg !text-sm nc-input-shadow !min-h-[120px] max-h-[500px] nc-scrollbar-thin" |
|
size="large" |
|
hide-details |
|
data-testid="nc-lock-view-description-input" |
|
:placeholder="$t('placeholder.lockViewDescription')" |
|
/> |
|
</a-form-item> |
|
</div> |
|
<div v-else class="flex flex-col gap-2"> |
|
<div class="text-base font-bold text-nc-content-gray-emphasis"> |
|
{{ $t('title.unlockViewTitle') }} |
|
</div> |
|
<div v-if="view?.meta?.lockedViewDescription" class="text-sm bg-nc-bg-gray-light rounded-lg px-2 py-2"> |
|
{{ view?.meta?.lockedViewDescription }} |
|
</div> |
|
<div class="text-sm text-nc-content-gray"> |
|
{{ $t('title.unlockViewTitleSubtitle') }} |
|
<span v-if="idUserMap[view?.meta?.lockedByUserId]?.id === user.id" class="font-bold"> {{ $t('general.you') }} </span> |
|
<span v-else class="font-bold"> |
|
{{ idUserMap[view?.meta?.lockedByUserId]?.display_name || idUserMap[view?.meta?.lockedByUserId]?.email }} |
|
</span> |
|
</div> |
|
</div> |
|
|
|
<div class="flex gap-2 items-center justify-end"> |
|
<NcButton type="secondary" size="small" :disabled="isLoading" data-testid="nc-cancel-btn" @click="dialogShow = false">{{ |
|
$t('general.cancel') |
|
}}</NcButton> |
|
|
|
<NcButton |
|
type="primary" |
|
html-type="submit" |
|
size="small" |
|
:loading="isLoading" |
|
:disabled="isLoading || isErrored" |
|
data-testid="nc-lock-or-unlock-btn" |
|
> |
|
<template #icon> |
|
<GeneralIcon :icon="changeType === LockType.Locked ? 'ncLock' : 'ncUnlock'" class="flex-none" /> |
|
</template> |
|
<div |
|
class="flex" |
|
:class="{ |
|
'-ml-1': !isLoading, |
|
}" |
|
> |
|
{{ changeType === LockType.Locked ? $t('labels.lockView') : $t('labels.unlockView') }} |
|
</div> |
|
</NcButton> |
|
</div> |
|
</a-form> |
|
</NcModal> |
|
</template> |
|
|
|
<style scoped lang="scss"> |
|
.ant-form-item { |
|
@apply mb-0; |
|
} |
|
|
|
.nc-unlock-view-wrapper { |
|
box-shadow: 0px 8px 8px -4px rgba(0, 0, 0, 0.04), 0px 20px 24px -4px rgba(0, 0, 0, 0.1); |
|
} |
|
</style>
|
|
|