diff --git a/web/tenant/src/components/common/ConfirmDialog.vue b/web/tenant/src/components/common/ConfirmDialog.vue
new file mode 100644
index 0000000..bb52860
--- /dev/null
+++ b/web/tenant/src/components/common/ConfirmDialog.vue
@@ -0,0 +1,27 @@
+
+
+ {{ options.message }}
+
+
+
+
+
+
+
+
+
diff --git a/web/tenant/src/composables/useConfirm.ts b/web/tenant/src/composables/useConfirm.ts
new file mode 100644
index 0000000..9a843fa
--- /dev/null
+++ b/web/tenant/src/composables/useConfirm.ts
@@ -0,0 +1,37 @@
+import { ref } from 'vue'
+
+interface ConfirmOptions {
+ title?: string
+ message: string
+ confirmText?: string
+ cancelText?: string
+ danger?: boolean
+}
+
+const visible = ref(false)
+const options = ref({ message: '' })
+let resolvePromise: ((value: boolean) => void) | null = null
+
+export function useConfirm() {
+ function confirm(opts: ConfirmOptions | string): Promise {
+ options.value = typeof opts === 'string' ? { message: opts } : opts
+ visible.value = true
+ return new Promise((resolve) => {
+ resolvePromise = resolve
+ })
+ }
+
+ function handleConfirm() {
+ visible.value = false
+ resolvePromise?.(true)
+ resolvePromise = null
+ }
+
+ function handleCancel() {
+ visible.value = false
+ resolvePromise?.(false)
+ resolvePromise = null
+ }
+
+ return { visible, options, confirm, handleConfirm, handleCancel }
+}