Confirmation Dialog
This component is a flexible, reusable confirmation dialog built with PrimeVue’s Dialog.
It supports optional input validation (reason field), customizable titles, icons, and action buttons.
It's useful for confirming user decisions such as deletions, submissions, or status changes.
🔧 Component: Dialog (PrimeVue)
Usage Summary
Controlled by prop: isOpen
Emits events: confirm and close
Optionally shows a reason textarea with validation
<!-- components/confirmation.vue -->
<template>
<Dialog
v-model:visible="dialogVisible"
modal
:show-header="false"
class="w-full max-w-md rounded-lg p-0 custom-modal"
>
<!-- Modal Header -->
<div class="flex items-center justify-between px-6 py-4 bg-neutral-20">
<div class="flex items-center gap-3">
<div v-if="icon" class="">
<BaseCustomIcon :name="icon" />
</div>
<p class="text-neutral-500 body-regular">{{ title }}</p>
</div>
<button @click="close">
<i
class="pi pi-times text-neutral-500 text-base hover:bg-primary-50 p-3 hover:rounded-full"
/>
</button>
</div>
<!-- Modal Body -->
<div class="px-6 py-6">
<div class=" mb-6 flex flex-col justify-center">
<h4 class="text-neutral-500 pb-4">{{ confirmationMessage }}</h4>
<p class="body-small text-neutral-l-700">
{{ informationText }}
</p>
</div>
<div v-if="requireReason" class="mt-4">
<FormText
:label="reasonLabel"
required
v-model="reason"
type="textarea"
:placeholder="reasonPlaceholder"
@input="validateField('reason')"
></FormText>
<span v-if="errors.reason" class="text-danger-300 mt-8 text-xs">
{{ errors.reason }}
</span>
</div>
</div>
<!-- Modal Footer -->
<div class="flex items-center gap-4 pt-4 pb-6 mx-6 border-t border-neutral-100">
<div class="w-full">
<CommonButton
:title="cancelButtonText"
bgColor="bg-neutral-30"
textColor="text-neutral-500 body-regular hover:bg-neutral-100 !h-10"
type="button"
@click="close"
/>
</div>
<div class="w-full">
<CommonButton
:title="actionButtonText"
:bgColor="actionButtonClass"
textColor="body-regular text-white !h-10"
type="submit"
:createIcon="btnIcon"
:disabled="requireReason && !reason.trim()"
@click="confirm"
/>
</div>
</div>
</Dialog>
</template>
<script setup>
import { ref, watch } from 'vue'
import FormText from '~/components/common/input/FormText.vue'
const props = defineProps({
isOpen: {
type: Boolean,
default: false
},
// More flexible props for custom dialogs
title: {
type: String,
default: 'Confirmation'
},
confirmationMessage: {
type: String,
default: 'Are you sure you want to proceed with this action?'
},
informationText: {
type: String,
default: 'This action cannot be undone.'
},
icon: {
type: String,
default: ''
},
btnIcon: {
type: String,
default: ''
},
// Button configuration
actionButtonText: {
type: String,
default: 'Confirm'
},
cancelButtonText: {
type: String,
default: 'Cancel'
},
actionButtonClass: {
type: String,
default: 'bg-primary-300 hover:bg-primary-200'
},
// Reason field configuration
requireReason: {
type: Boolean,
default: false
},
reasonLabel: {
type: String,
default: 'Give a reason'
},
reasonPlaceholder: {
type: String,
default: 'Enter reason'
},
// Optional - for backward compatibility
type: {
type: String,
default: ''
},
entityType: {
type: String,
default: ''
}
})
const emit = defineEmits(['close', 'confirm'])
const reason = ref('')
const dialogVisible = ref(false)
const errors = ref({
reason: ''
})
// Watch for changes in the isOpen prop
watch(
() => props.isOpen,
(newVal) => {
dialogVisible.value = newVal
}
)
// Methods
const close = () => {
reason.value = ''
errors.value = { reason: '' }
dialogVisible.value = false
emit('close')
}
const confirm = () => {
emit('confirm', {
type: props.type,
reason: reason.value,
entityType: props.entityType
})
reason.value = ''
dialogVisible.value = false
}
const validateField = (field) => {
if (field === 'reason') {
errors.value.reason = !reason.value.trim() ? 'Reason is required' : ''
}
}
</script>
How to use
<CommonConfirmation
:isOpen="isModalOpen"
:title="modalTitle"
:confirmationMessage="customMessage"
:informationText="customInfo"
:actionButtonText="modalActionText"
:actionButtonClass="modalActionClass"
:requireReason="requireReason"
:btnIcon="btnIcon"
@close="closeModal"
@confirm="handleConfirmation"
/>
<script setup>
// Modal configuration
const isModalOpen = ref(false)
const modalTitle = ref('Confirmation')
const customMessage = ref('')
const customInfo = ref('')
const modalActionText = ref('Confirm')
const modalActionClass = ref('bg-primary-300 hover:bg-primary-200')
const requireReason = ref(false)
const btnIcon = ref('')
// Open approve modal
// Open activate modal
const onActivate = () => {
modalTitle.value = 'Reactivate Team Member'
modalActionText.value = 'Reactivate'
modalActionClass.value = 'bg-[#0DBF66] hover:bg-success-200'
requireReason.value = false
customMessage.value = 'Are you sure you want to reactivate this team member?'
customInfo.value = "This will give them access to the admin platform. You can suspend them at any time."
isModalOpen.value = true
btnIcon.value = 'check'
}
// Open suspend modal
const onsuspend = () => {
modalTitle.value = 'Suspend Team Member'
modalActionText.value = 'Suspend'
modalActionClass.value = 'bg-[#ED5556] hover:bg-danger-200'
requireReason.value = false
customMessage.value = 'Are you sure you want to suspend this team member?'
customInfo.value =
"This will prevent them from accessing the admin platform. You can reactivate them at any time."
isModalOpen.value = true
btnIcon.value = 'cancel'
}
// Close modal
const closeModal = () => {
isModalOpen.value = false
}
// Handle confirmation
const handleConfirmation = (data) => {
console.log('Confirmation data:', data)
// Here you would handle the approve/reject action
// For example, make API calls based on data.type and data.reason
// Handle the action based on modalTitle.value or other contextual info
if (modalTitle.value === 'Activate Member') {
// Handle activation
} else if (modalTitle.value === 'Suspend Member') {
// Handle suspension
}
closeModal()
}
</script>