Update Tier Drawer

This commit is contained in:
efrilm 2025-09-18 01:21:51 +07:00
parent 6d2e436b09
commit 3a56e56c69

View File

@ -32,9 +32,40 @@ type Props = {
data?: Tier // Data tier untuk edit (jika ada)
}
// Static benefit keys with their configurations
const STATIC_BENEFIT_KEYS = {
birthday_bonus: {
label: 'Birthday Bonus',
type: 'boolean' as const,
description: 'Bonus ulang tahun khusus member'
},
exclusive_discounts: {
label: 'Exclusive Discounts',
type: 'boolean' as const,
description: 'Akses diskon eksklusif'
},
point_multiplier: {
label: 'Point Multiplier',
type: 'number' as const,
description: 'Pengali poin (contoh: 1.1 = +10%)',
suffix: 'x'
},
priority_support: {
label: 'Priority Support',
type: 'boolean' as const,
description: 'Dukungan pelanggan prioritas'
},
special_discount: {
label: 'Special Discount',
type: 'number' as const,
description: 'Diskon khusus dalam persen',
suffix: '%'
}
} as const
// Benefit item type
type BenefitItem = {
key: string
key: keyof typeof STATIC_BENEFIT_KEYS
value: any
type: 'boolean' | 'number' | 'string'
}
@ -43,19 +74,13 @@ type FormValidateType = {
name: string
min_points: number
benefits: BenefitItem[]
newBenefitKey: string
newBenefitValue: string
newBenefitType: 'boolean' | 'number' | 'string'
}
// Initial form data
const initialData: FormValidateType = {
name: '',
min_points: 0,
benefits: [],
newBenefitKey: '',
newBenefitValue: '',
newBenefitType: 'boolean'
benefits: []
}
const AddEditTierDrawer = (props: Props) => {
@ -84,17 +109,16 @@ const AddEditTierDrawer = (props: Props) => {
})
const watchedBenefits = watch('benefits')
const watchedNewBenefitKey = watch('newBenefitKey')
const watchedNewBenefitValue = watch('newBenefitValue')
const watchedNewBenefitType = watch('newBenefitType')
// Helper function to convert benefits object to BenefitItem array
const convertBenefitsToArray = (benefits: Record<string, any>): BenefitItem[] => {
if (!benefits) return []
return Object.entries(benefits).map(([key, value]) => ({
key,
return Object.entries(benefits)
.filter(([key]) => key in STATIC_BENEFIT_KEYS)
.map(([key, value]) => ({
key: key as keyof typeof STATIC_BENEFIT_KEYS,
value,
type: typeof value === 'boolean' ? 'boolean' : typeof value === 'number' ? 'number' : 'string'
type: STATIC_BENEFIT_KEYS[key as keyof typeof STATIC_BENEFIT_KEYS].type
}))
}
@ -116,19 +140,26 @@ const AddEditTierDrawer = (props: Props) => {
// Helper function to format benefit display
const formatBenefitDisplay = (item: BenefitItem): string => {
const readableKey = item.key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())
const config = STATIC_BENEFIT_KEYS[item.key]
if (item.type === 'boolean') {
return `${readableKey}: ${item.value ? 'Ya' : 'Tidak'}`
return `${config.label}: ${item.value ? 'Ya' : 'Tidak'}`
} else if (item.type === 'number') {
if (item.key.includes('multiplier')) {
return `${readableKey}: ${item.value}x`
} else if (item.key.includes('discount') || item.key.includes('bonus')) {
return `${readableKey}: ${item.value}%`
const suffix = config.suffix || ''
return `${config.label}: ${item.value}${suffix}`
}
return `${readableKey}: ${item.value}`
return `${config.label}: ${item.value}`
}
return `${readableKey}: ${item.value}`
// Get available benefit keys (not already added)
const getAvailableBenefitKeys = () => {
const usedKeys = watchedBenefits?.map(b => b.key) || []
return Object.entries(STATIC_BENEFIT_KEYS)
.filter(([key]) => !usedKeys.includes(key as keyof typeof STATIC_BENEFIT_KEYS))
.map(([key, config]) => ({
key: key as keyof typeof STATIC_BENEFIT_KEYS,
...config
}))
}
// Effect to populate form when editing
@ -141,10 +172,7 @@ const AddEditTierDrawer = (props: Props) => {
const formData: FormValidateType = {
name: data.name || '',
min_points: data.min_points || 0,
benefits: benefitsArray,
newBenefitKey: '',
newBenefitValue: '',
newBenefitType: 'boolean'
benefits: benefitsArray
}
resetForm(formData)
@ -156,57 +184,6 @@ const AddEditTierDrawer = (props: Props) => {
}
}, [data, isEditMode, resetForm])
const handleAddBenefit = () => {
const key = watchedNewBenefitKey.trim()
const value = watchedNewBenefitValue.trim()
const type = watchedNewBenefitType
if (key && value) {
// Check if key already exists
const existingKeys = watchedBenefits.map(b => b.key)
if (existingKeys.includes(key)) {
alert('Key benefit sudah ada!')
return
}
let processedValue: any = value
if (type === 'boolean') {
processedValue = value === 'true' || value === 'yes' || value === '1'
} else if (type === 'number') {
processedValue = Number(value)
if (isNaN(processedValue)) {
alert('Nilai harus berupa angka!')
return
}
}
const newBenefit: BenefitItem = {
key,
value: processedValue,
type
}
const currentBenefits = watchedBenefits || []
setValue('benefits', [...currentBenefits, newBenefit])
setValue('newBenefitKey', '')
setValue('newBenefitValue', '')
setValue('newBenefitType', 'boolean')
}
}
const handleRemoveBenefit = (index: number) => {
const currentBenefits = watchedBenefits || []
const newBenefits = currentBenefits.filter((_, i) => i !== index)
setValue('benefits', newBenefits)
}
const handleKeyPress = (event: React.KeyboardEvent) => {
if (event.key === 'Enter') {
event.preventDefault()
handleAddBenefit()
}
}
const handleFormSubmit = async (formData: FormValidateType) => {
try {
setIsSubmitting(true)
@ -257,6 +234,11 @@ const AddEditTierDrawer = (props: Props) => {
setShowMore(false)
}
// Get placeholder and validation info based on selected benefit key
const getBenefitInputInfo = () => {
return { placeholder: 'Tidak diperlukan lagi', type: 'text' }
}
const formatNumber = (value: number) => {
return new Intl.NumberFormat('id-ID').format(value)
}
@ -357,109 +339,133 @@ const AddEditTierDrawer = (props: Props) => {
{/* Benefits */}
<div>
<Typography variant='body2' className='mb-2'>
<Typography variant='body2' className='mb-3'>
Manfaat Tier <span className='text-red-500'>*</span>
</Typography>
{/* Display current benefits */}
{watchedBenefits && watchedBenefits.length > 0 && (
<div className='flex flex-col gap-2 mb-3'>
{watchedBenefits.map((benefit, index) => (
<Chip
key={index}
label={formatBenefitDisplay(benefit)}
onDelete={() => handleRemoveBenefit(index)}
color='primary'
variant='outlined'
size='small'
sx={{
justifyContent: 'space-between',
'& .MuiChip-label': {
overflow: 'visible',
textOverflow: 'unset',
whiteSpace: 'normal'
{/* All Benefits in Horizontal Layout */}
<div className='space-y-4'>
{Object.entries(STATIC_BENEFIT_KEYS).map(([key, config]) => {
const benefitKey = key as keyof typeof STATIC_BENEFIT_KEYS
const existingBenefit = watchedBenefits?.find(b => b.key === benefitKey)
const isActive = Boolean(existingBenefit)
return (
<div key={benefitKey} className='border rounded-lg p-3'>
<div className='flex items-center justify-between mb-2'>
<div className='flex-1'>
<Typography variant='body2' className='font-medium'>
{config.label}
</Typography>
<Typography variant='caption' color='text.secondary'>
{config.description}
</Typography>
</div>
<FormControlLabel
control={
<Switch
checked={isActive}
onChange={e => {
if (e.target.checked) {
// Add default benefit
const defaultValue =
config.type === 'boolean'
? true
: config.type === 'number'
? benefitKey === 'point_multiplier'
? 1.1
: benefitKey === 'special_discount'
? 5
: 1
: ''
const newBenefit: BenefitItem = {
key: benefitKey,
value: defaultValue,
type: config.type
}
const currentBenefits = watchedBenefits || []
setValue('benefits', [...currentBenefits, newBenefit])
} else {
// Remove benefit
const currentBenefits = watchedBenefits || []
const newBenefits = currentBenefits.filter(b => b.key !== benefitKey)
setValue('benefits', newBenefits)
}
}}
/>
))}
</div>
)}
{/* Add new benefit - Key */}
<div className='mb-3'>
<Controller
name='newBenefitKey'
control={control}
render={({ field }) => (
<CustomTextField
{...field}
fullWidth
placeholder='Key benefit (contoh: birthday_bonus, point_multiplier)'
label='Key Benefit'
size='small'
/>
)}
}
label=''
sx={{ margin: 0 }}
/>
</div>
{/* Type selector */}
<div className='mb-3'>
<Controller
name='newBenefitType'
control={control}
render={({ field }) => (
<FormControl fullWidth size='small'>
<InputLabel>Tipe Value</InputLabel>
<Select {...field} label='Tipe Value'>
<MenuItem value='boolean'>Boolean (Ya/Tidak)</MenuItem>
<MenuItem value='number'>Number (Angka)</MenuItem>
<MenuItem value='string'>String (Teks)</MenuItem>
{/* Value Input - Only show when active */}
{isActive && (
<div className='mt-2'>
{config.type === 'boolean' ? (
<FormControl size='small' fullWidth>
<Select
value={existingBenefit?.value ? 'true' : 'false'}
onChange={e => {
const currentBenefits = watchedBenefits || []
const updatedBenefits = currentBenefits.map(b =>
b.key === benefitKey ? { ...b, value: e.target.value === 'true' } : b
)
setValue('benefits', updatedBenefits)
}}
>
<MenuItem value='true'>Ya</MenuItem>
<MenuItem value='false'>Tidak</MenuItem>
</Select>
</FormControl>
)}
/>
</div>
{/* Add new benefit - Value */}
<div className='mb-3'>
<Controller
name='newBenefitValue'
control={control}
render={({ field }) => (
) : (
<CustomTextField
{...field}
fullWidth
placeholder={
watchedNewBenefitType === 'boolean'
? 'true/false, yes/no, 1/0'
: watchedNewBenefitType === 'number'
? 'Contoh: 1.5, 10, 5'
: 'Contoh: Premium access, VIP status'
}
label='Value Benefit'
size='small'
onKeyPress={handleKeyPress}
InputProps={{
endAdornment: (
<InputAdornment position='end'>
<Button
size='small'
onClick={handleAddBenefit}
disabled={!watchedNewBenefitKey?.trim() || !watchedNewBenefitValue?.trim()}
>
Tambah
</Button>
</InputAdornment>
type='number'
value={existingBenefit?.value || ''}
onChange={e => {
const newValue = Number(e.target.value)
if (!isNaN(newValue)) {
const currentBenefits = watchedBenefits || []
const updatedBenefits = currentBenefits.map(b =>
b.key === benefitKey ? { ...b, value: newValue } : b
)
setValue('benefits', updatedBenefits)
}
}}
placeholder={
benefitKey === 'point_multiplier'
? 'Contoh: 1.1, 1.5, 2.0'
: benefitKey === 'special_discount'
? 'Contoh: 5, 10, 15'
: 'Masukkan angka'
}
InputProps={{
endAdornment: config.suffix && (
<InputAdornment position='end'>{config.suffix}</InputAdornment>
),
inputProps: {
step: benefitKey === 'point_multiplier' ? '0.1' : '1',
min: benefitKey === 'point_multiplier' ? '0.1' : '0',
max: benefitKey === 'special_discount' ? '100' : undefined
}
}}
/>
)}
/>
</div>
)}
</div>
)
})}
</div>
{(!watchedBenefits || watchedBenefits.length === 0) && (
<Typography variant='caption' color='error'>
Minimal satu manfaat harus ditambahkan
<Typography variant='caption' color='error' className='mt-2 block'>
Minimal satu manfaat harus diaktifkan
</Typography>
)}
</div>