Files
Khafid Prayoga 3fbcdf9e2a refactor(treatment-report): restructure treatment report form and components
- Replace SelectDPJP with SelectDoctor component
- Update schema naming from ActionReport to TreatmentReport
- Add doctor selection functionality to treatment report form
- Improve form layout and field organization
- Update related model imports to use single quotes
- add fragment for better form grouping
- cherry pick form field from another branch
2025-11-25 17:15:00 +07:00

166 lines
4.2 KiB
Vue

<script setup lang="ts">
import { Button } from '~/components/pub/ui/button'
import { cn } from '~/lib/utils'
/**
* Button Action Component untuk form
* Support preset: add, delete, save, cancel
*/
interface Props {
/**
* Preset button type dengan styling bawaan
* - add: Button tambah (outline primary)
* - delete: Button hapus (ghost)
* - save: Button simpan (primary)
* - cancel: Button batal (secondary)
* - custom: Custom styling
*/
preset?: 'add' | 'delete' | 'save' | 'cancel' | 'custom'
/**
* Icon name (UnoCSS/Lucide)
* Default akan diset berdasarkan preset
*/
icon?: string
/**
* Button text
* Set ke empty string ('') untuk icon-only mode
*/
label?: string
/**
* Button title (tooltip)
* Wajib untuk icon-only buttons (accessibility)
*/
title?: string
/**
* Icon only mode (no label)
* Otomatis true jika label kosong
*/
iconOnly?: boolean
/**
* Button type
*/
type?: 'button' | 'submit' | 'reset'
/**
* Disabled state
*/
disabled?: boolean
/**
* Custom class untuk override styling
*/
class?: string
/**
* Responsive width (full width on mobile)
*/
fullWidthMobile?: boolean
/**
* Button size
*/
size?: 'default' | 'sm' | 'lg' | 'icon'
/**
* Button variant (override preset variant)
*/
variant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link'
}
const props = withDefaults(defineProps<Props>(), {
preset: 'custom',
type: 'button',
disabled: false,
fullWidthMobile: false,
iconOnly: false,
})
// Preset configurations
const presetConfig = {
add: {
variant: 'outline' as const,
icon: 'i-lucide-plus',
label: 'Tambah',
classes:
'border-primary bg-white text-primary hover:bg-primary hover:text-white dark:bg-slate-800 dark:border-primary dark:text-primary dark:hover:bg-primary dark:hover:text-white',
},
delete: {
variant: 'ghost' as const,
icon: 'i-lucide-trash-2',
label: '', // Default kosong untuk icon-only
classes:
'hover:bg-destructive hover:text-white hover:border-destructive dark:hover:bg-destructive dark:hover:text-white',
},
save: {
variant: 'default' as const,
icon: 'i-lucide-save',
label: 'Simpan',
classes: 'bg-primary text-primary-foreground hover:bg-primary/90 dark:bg-primary dark:hover:bg-primary/90',
},
cancel: {
variant: 'secondary' as const,
icon: 'i-lucide-x',
label: 'Batal',
classes: 'bg-secondary text-secondary-foreground hover:bg-secondary/80 dark:bg-slate-700 dark:hover:bg-slate-600',
},
custom: {
variant: 'default' as const,
icon: '',
label: '',
classes: '',
},
}
const currentPreset = computed(() => presetConfig[props.preset])
const buttonVariant = computed(() => props.variant || currentPreset.value.variant)
const buttonIcon = computed(() => props.icon || currentPreset.value.icon)
// Label handling: gunakan prop label jika ada, fallback ke preset, atau undefined jika iconOnly
const buttonLabel = computed(() => {
if (props.label !== undefined) return props.label
return currentPreset.value.label
})
const buttonTitle = computed(() => props.title || buttonLabel.value)
// Deteksi icon-only mode
const isIconOnly = computed(() => {
return props.iconOnly || buttonLabel.value === '' || !buttonLabel.value
})
const buttonClasses = computed(() => {
// Base classes berbeda untuk icon-only vs with-label
const baseClasses = isIconOnly.value
? 'rounded-md p-2 w-9 h-9 transition-colors flex items-center justify-center' // Icon only: square button
: 'rounded-md px-4 py-2 transition-colors sm:text-sm' // With label: padding horizontal lebih besar
const widthClasses = props.fullWidthMobile && !isIconOnly.value ? 'w-full sm:w-auto' : ''
const presetClasses = currentPreset.value.classes
return cn(baseClasses, widthClasses, presetClasses, props.class)
})
</script>
<template>
<Button
:title="buttonTitle"
:type="type"
:variant="buttonVariant"
:size="size"
:disabled="disabled"
:class="buttonClasses"
>
<Icon
v-if="buttonIcon"
:name="buttonIcon"
:class="cn('h-4 w-4 align-middle transition-colors', !isIconOnly ? 'mr-2' : '')"
/>
<slot v-if="!isIconOnly">{{ buttonLabel }}</slot>
</Button>
</template>