dev: hotfix refactor
+ merged pub/custom-ui and pub/base into my-ui - droped pub/custom-ui
This commit is contained in:
No files matched your search
@@ -0,0 +1,137 @@
|
||||
# Confirmation Modal Components
|
||||
|
||||
Sistem confirmation modal yang modular dan dapat digunakan kembali di seluruh aplikasi.
|
||||
|
||||
## Components
|
||||
|
||||
### 1. `confirmation.vue` - Base Confirmation Modal
|
||||
|
||||
Komponen dasar untuk modal konfirmasi yang dapat dikustomisasi.
|
||||
|
||||
**Props:**
|
||||
- `open?: boolean` - Status modal (terbuka/tertutup)
|
||||
- `title?: string` - Judul modal (default: "Konfirmasi")
|
||||
- `message?: string` - Pesan konfirmasi (default: "Apakah Anda yakin ingin melanjutkan?")
|
||||
- `confirmText?: string` - Text tombol konfirmasi (default: "Ya")
|
||||
- `cancelText?: string` - Text tombol batal (default: "Batal")
|
||||
- `variant?: 'default' | 'destructive' | 'warning'` - Varian tampilan (default: "default")
|
||||
- `size?: 'sm' | 'md' | 'lg' | 'xl'` - Ukuran modal (default: "md")
|
||||
|
||||
**Events:**
|
||||
- `@confirm` - Dipanggil saat tombol konfirmasi diklik
|
||||
- `@cancel` - Dipanggil saat tombol batal diklik
|
||||
- `@update:open` - Dipanggil saat status modal berubah
|
||||
|
||||
**Contoh penggunaan:**
|
||||
```vue
|
||||
<template>
|
||||
<Confirmation
|
||||
v-model:open="isConfirmOpen"
|
||||
title="Hapus Data"
|
||||
message="Apakah Anda yakin ingin menghapus data ini?"
|
||||
confirm-text="Hapus"
|
||||
variant="destructive"
|
||||
@confirm="handleConfirm"
|
||||
@cancel="handleCancel"
|
||||
/>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 2. `record-confirmation.vue` - Record-specific Confirmation
|
||||
|
||||
Komponen khusus untuk konfirmasi operasi pada record/data tertentu.
|
||||
|
||||
**Props:**
|
||||
- `open?: boolean` - Status modal
|
||||
- `action?: 'delete' | 'deactivate' | 'activate' | 'archive' | 'restore'` - Jenis aksi (default: "delete")
|
||||
- `record?: RecordData | null` - Data record yang akan diproses
|
||||
- `customTitle?: string` - Custom judul (opsional)
|
||||
- `customMessage?: string` - Custom pesan (opsional)
|
||||
- `customConfirmText?: string` - Custom text tombol konfirmasi (opsional)
|
||||
- `customCancelText?: string` - Custom text tombol batal (opsional)
|
||||
|
||||
**Events:**
|
||||
- `@confirm` - Dipanggil dengan parameter `(record, action)`
|
||||
- `@cancel` - Dipanggil saat batal
|
||||
- `@update:open` - Update status modal
|
||||
|
||||
**Contoh penggunaan:**
|
||||
```vue
|
||||
<template>
|
||||
<RecordConfirmation
|
||||
v-model:open="isRecordConfirmOpen"
|
||||
action="delete"
|
||||
:record="selectedRecord"
|
||||
@confirm="handleDeleteRecord"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<template #default="{ record }">
|
||||
<div class="text-sm">
|
||||
<p><strong>ID:</strong> {{ record?.id }}</p>
|
||||
<p><strong>Nama:</strong> {{ record?.name }}</p>
|
||||
</div>
|
||||
</template>
|
||||
</RecordConfirmation>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Action Types
|
||||
|
||||
Record confirmation mendukung beberapa jenis aksi dengan konfigurasi default:
|
||||
|
||||
- **delete**: Hapus data (variant: destructive, warna merah)
|
||||
- **deactivate**: Nonaktifkan data (variant: warning, warna kuning)
|
||||
- **activate**: Aktifkan data (variant: default, warna biru)
|
||||
- **archive**: Arsipkan data (variant: warning, warna kuning)
|
||||
- **restore**: Pulihkan data (variant: default, warna biru)
|
||||
|
||||
## Integration Example
|
||||
|
||||
Contoh implementasi di komponen list:
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
// State management
|
||||
const isRecordConfirmationOpen = ref(false)
|
||||
const selectedRecord = ref(null)
|
||||
const confirmAction = ref('delete')
|
||||
|
||||
// Handle action dari table
|
||||
function handleRowAction(action, record) {
|
||||
selectedRecord.value = record
|
||||
confirmAction.value = action
|
||||
isRecordConfirmationOpen.value = true
|
||||
}
|
||||
|
||||
// Handle konfirmasi
|
||||
async function handleConfirmAction(record, action) {
|
||||
try {
|
||||
// API call berdasarkan action
|
||||
await performAction(action, record.id)
|
||||
// Refresh data
|
||||
await refreshData()
|
||||
} catch (error) {
|
||||
// Handle error
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- Your list component -->
|
||||
<DataTable @action="handleRowAction" />
|
||||
|
||||
<!-- Confirmation modal -->
|
||||
<RecordConfirmation
|
||||
v-model:open="isRecordConfirmationOpen"
|
||||
:action="confirmAction"
|
||||
:record="selectedRecord"
|
||||
@confirm="handleConfirmAction"
|
||||
/>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Styling
|
||||
|
||||
Komponen menggunakan Tailwind CSS dan shadcn/ui components. Pastikan dependencies berikut tersedia:
|
||||
- `~/components/pub/my-ui/modal/dialog.vue`
|
||||
- `~/components/pub/ui/button`
|
||||
@@ -0,0 +1,99 @@
|
||||
<script setup lang="ts">
|
||||
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
||||
import { Button } from '~/components/pub/ui/button'
|
||||
|
||||
interface ConfirmationProps {
|
||||
open?: boolean
|
||||
title?: string
|
||||
message?: string
|
||||
confirmText?: string
|
||||
cancelText?: string
|
||||
variant?: 'default' | 'destructive' | 'warning'
|
||||
size?: 'sm' | 'md' | 'lg' | 'xl'
|
||||
}
|
||||
|
||||
interface ConfirmationEmits {
|
||||
'update:open': [value: boolean]
|
||||
'confirm': []
|
||||
'cancel': []
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<ConfirmationProps>(), {
|
||||
open: false,
|
||||
title: 'Konfirmasi',
|
||||
message: 'Apakah Anda yakin ingin melanjutkan?',
|
||||
confirmText: 'Ya',
|
||||
cancelText: 'Batal',
|
||||
variant: 'default',
|
||||
size: 'md',
|
||||
})
|
||||
|
||||
const emit = defineEmits<ConfirmationEmits>()
|
||||
|
||||
const isOpen = computed({
|
||||
get: () => props.open,
|
||||
set: (value) => emit('update:open', value),
|
||||
})
|
||||
|
||||
const variantClasses = computed(() => {
|
||||
const variants = {
|
||||
default: {
|
||||
icon: 'i-lucide-help-circle',
|
||||
iconColor: 'text-blue-500',
|
||||
confirmVariant: 'default' as const,
|
||||
},
|
||||
destructive: {
|
||||
icon: 'i-lucide-alert-triangle',
|
||||
iconColor: 'text-red-500',
|
||||
confirmVariant: 'destructive' as const,
|
||||
},
|
||||
warning: {
|
||||
icon: 'i-lucide-alert-circle',
|
||||
iconColor: 'text-yellow-500',
|
||||
confirmVariant: 'default' as const,
|
||||
},
|
||||
}
|
||||
return variants[props.variant]
|
||||
})
|
||||
|
||||
function handleConfirm() {
|
||||
emit('confirm')
|
||||
emit('update:open', false)
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
emit('cancel')
|
||||
emit('update:open', false)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Dialog v-model:open="isOpen" :title="title" :size="size">
|
||||
<div class="space-y-4">
|
||||
<!-- Icon dan pesan -->
|
||||
<div class="flex items-start gap-3">
|
||||
<div :class="[variantClasses.icon, variantClasses.iconColor]" class="w-6 h-6 mt-1 flex-shrink-0" />
|
||||
<div class="flex-1">
|
||||
<p class="text-sm text-muted-foreground leading-relaxed">
|
||||
{{ message }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slot untuk konten custom -->
|
||||
<div v-if="$slots.default">
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<!-- Footer buttons -->
|
||||
<div class="flex justify-end gap-3 pt-4">
|
||||
<Button variant="outline" @click="handleCancel">
|
||||
{{ cancelText }}
|
||||
</Button>
|
||||
<Button :variant="variantClasses.confirmVariant" @click="handleConfirm">
|
||||
{{ confirmText }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</template>
|
||||
@@ -0,0 +1,131 @@
|
||||
<script setup lang="ts">
|
||||
import Confirmation from '~/components/pub/my-ui/confirmation/confirmation.vue'
|
||||
|
||||
interface RecordData {
|
||||
id: number | string
|
||||
name?: string
|
||||
title?: string
|
||||
code?: string
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
interface RecordConfirmationProps {
|
||||
open?: boolean
|
||||
action?: 'delete' | 'deactivate' | 'activate' | 'archive' | 'restore'
|
||||
record?: RecordData | null
|
||||
customTitle?: string
|
||||
customMessage?: string
|
||||
customConfirmText?: string
|
||||
customCancelText?: string
|
||||
}
|
||||
|
||||
interface RecordConfirmationEmits {
|
||||
'update:open': [value: boolean]
|
||||
'confirm': [record: RecordData, action: string]
|
||||
'cancel': []
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<RecordConfirmationProps>(), {
|
||||
open: false,
|
||||
action: 'delete',
|
||||
record: null,
|
||||
customTitle: '',
|
||||
customMessage: '',
|
||||
customConfirmText: '',
|
||||
customCancelText: '',
|
||||
})
|
||||
|
||||
const emit = defineEmits<RecordConfirmationEmits>()
|
||||
|
||||
const isOpen = computed({
|
||||
get: () => props.open,
|
||||
set: (value) => emit('update:open', value),
|
||||
})
|
||||
|
||||
const actionConfig = computed(() => {
|
||||
const configs = {
|
||||
delete: {
|
||||
title: 'Hapus Data',
|
||||
message: 'Apakah Anda yakin ingin menghapus data ini? Tindakan ini tidak dapat dibatalkan.',
|
||||
confirmText: 'Hapus',
|
||||
variant: 'destructive' as const,
|
||||
},
|
||||
deactivate: {
|
||||
title: 'Nonaktifkan Data',
|
||||
message: 'Apakah Anda yakin ingin menonaktifkan data ini?',
|
||||
confirmText: 'Nonaktifkan',
|
||||
variant: 'warning' as const,
|
||||
},
|
||||
activate: {
|
||||
title: 'Aktifkan Data',
|
||||
message: 'Apakah Anda yakin ingin mengaktifkan data ini?',
|
||||
confirmText: 'Aktifkan',
|
||||
variant: 'default' as const,
|
||||
},
|
||||
archive: {
|
||||
title: 'Arsipkan Data',
|
||||
message: 'Apakah Anda yakin ingin mengarsipkan data ini?',
|
||||
confirmText: 'Arsipkan',
|
||||
variant: 'warning' as const,
|
||||
},
|
||||
restore: {
|
||||
title: 'Pulihkan Data',
|
||||
message: 'Apakah Anda yakin ingin memulihkan data ini?',
|
||||
confirmText: 'Pulihkan',
|
||||
variant: 'default' as const,
|
||||
},
|
||||
}
|
||||
return configs[props.action]
|
||||
})
|
||||
|
||||
const finalTitle = computed(() => {
|
||||
return props.customTitle || actionConfig.value.title
|
||||
})
|
||||
|
||||
const finalMessage = computed(() => {
|
||||
if (props.customMessage) {
|
||||
return props.customMessage
|
||||
}
|
||||
|
||||
const baseMessage = actionConfig.value.message
|
||||
// if (props.record) {
|
||||
// const recordName = props.record.name || props.record.title || props.record.code || `ID: ${props.record.id}`
|
||||
// return `${baseMessage}\n\nData: ${recordName}`
|
||||
// }
|
||||
|
||||
return baseMessage
|
||||
})
|
||||
|
||||
const finalConfirmText = computed(() => {
|
||||
return props.customConfirmText || actionConfig.value.confirmText
|
||||
})
|
||||
|
||||
const finalCancelText = computed(() => {
|
||||
return props.customCancelText || 'Batal'
|
||||
})
|
||||
|
||||
function handleConfirm() {
|
||||
if (props.record) {
|
||||
emit('confirm', props.record, props.action)
|
||||
}
|
||||
emit('update:open', false)
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
emit('cancel')
|
||||
emit('update:open', false)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Confirmation
|
||||
v-model:open="isOpen" :title="finalTitle" :message="finalMessage" :confirm-text="finalConfirmText"
|
||||
:cancel-text="finalCancelText" :variant="actionConfig.variant" size="md" @confirm="handleConfirm"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<!-- Slot untuk informasi tambahan record -->
|
||||
<div v-if="record && $slots.default" class="mt-4 p-3 bg-muted rounded-md">
|
||||
<slot :record="record" :action="action" />
|
||||
</div>
|
||||
</Confirmation>
|
||||
</template>
|
||||
Reference in New Issue
Block a user