Ingredient Detail

This commit is contained in:
efrilm 2025-09-11 16:22:05 +07:00
parent 29defb68c8
commit 5a77d3c2ea
2 changed files with 431 additions and 30 deletions

View File

@ -0,0 +1,370 @@
'use client'
// React Imports
import { useState } from 'react'
// MUI Imports
import Button from '@mui/material/Button'
import Drawer from '@mui/material/Drawer'
import IconButton from '@mui/material/IconButton'
import MenuItem from '@mui/material/MenuItem'
import Typography from '@mui/material/Typography'
import Divider from '@mui/material/Divider'
import Grid from '@mui/material/Grid2'
import Box from '@mui/material/Box'
// Third-party Imports
import { useForm, Controller } from 'react-hook-form'
// Component Imports
import CustomTextField from '@core/components/mui/TextField'
type Props = {
open: boolean
handleClose: () => void
setData?: (data: any) => void
}
type UnitConversionType = {
satuan: string
quantity: number
unit: string
hargaBeli: number
hargaJual: number
isDefault: boolean
}
type FormValidateType = {
conversions: UnitConversionType[]
}
// Vars
const initialConversion: UnitConversionType = {
satuan: 'Box',
quantity: 12,
unit: 'Pcs',
hargaBeli: 3588000,
hargaJual: 5988000,
isDefault: false
}
const IngedientUnitConversionDrawer = (props: Props) => {
// Props
const { open, handleClose, setData } = props
// States
const [conversions, setConversions] = useState<UnitConversionType[]>([initialConversion])
// Hooks
const {
control,
reset: resetForm,
handleSubmit,
formState: { errors }
} = useForm<FormValidateType>({
defaultValues: {
conversions: [initialConversion]
}
})
// Functions untuk konversi unit
const handleTambahBaris = () => {
const newConversion: UnitConversionType = {
satuan: '',
quantity: 0,
unit: '',
hargaBeli: 0,
hargaJual: 0,
isDefault: false
}
setConversions([...conversions, newConversion])
}
const handleHapusBaris = (index: number) => {
if (conversions.length > 1) {
const newConversions = conversions.filter((_, i) => i !== index)
setConversions(newConversions)
}
}
const handleChangeConversion = (index: number, field: keyof UnitConversionType, value: any) => {
const newConversions = [...conversions]
newConversions[index] = { ...newConversions[index], [field]: value }
setConversions(newConversions)
}
const handleToggleDefault = (index: number) => {
const newConversions = conversions.map((conversion, i) => ({
...conversion,
isDefault: i === index
}))
setConversions(newConversions)
}
const onSubmit = (data: FormValidateType) => {
console.log('Unit conversions:', conversions)
if (setData) {
setData(conversions)
}
handleClose()
}
const handleReset = () => {
handleClose()
setConversions([initialConversion])
resetForm({ conversions: [initialConversion] })
}
const formatNumber = (value: number) => {
return new Intl.NumberFormat('id-ID').format(value)
}
const parseNumber = (value: string) => {
return parseInt(value.replace(/\./g, '')) || 0
}
return (
<Drawer
open={open}
anchor='right'
variant='temporary'
onClose={handleReset}
ModalProps={{ keepMounted: true }}
sx={{
'& .MuiDrawer-paper': {
width: { xs: 350, sm: 800 },
display: 'flex',
flexDirection: 'column',
height: '100%'
}
}}
>
{/* Sticky Header */}
<Box
sx={{
position: 'sticky',
top: 0,
zIndex: 10,
backgroundColor: 'background.paper',
borderBottom: 1,
borderColor: 'divider'
}}
>
<div className='flex items-center justify-between plb-5 pli-6'>
<Typography variant='h5'>Konversi Unit Bahan</Typography>
<IconButton size='small' onClick={handleReset}>
<i className='tabler-x text-2xl text-textPrimary' />
</IconButton>
</div>
</Box>
{/* Scrollable Content */}
<Box sx={{ flex: 1, overflowY: 'auto' }}>
<form id='unit-conversion-form' onSubmit={handleSubmit(data => onSubmit(data))}>
<div className='flex flex-col gap-6 p-6'>
{/* Header Kolom */}
<Grid container spacing={2} alignItems='center' className='bg-gray-50 p-3 rounded-lg'>
<Grid size={2}>
<Typography variant='body2' fontWeight='medium'>
Satuan
</Typography>
</Grid>
<Grid size={1} className='text-center'>
<Typography variant='body2' fontWeight='medium'>
=
</Typography>
</Grid>
<Grid size={1.5}>
<Typography variant='body2' fontWeight='medium'>
Jumlah
</Typography>
</Grid>
<Grid size={1.5}>
<Typography variant='body2' fontWeight='medium'>
Unit
</Typography>
</Grid>
<Grid size={2}>
<Typography variant='body2' fontWeight='medium'>
Harga Beli
</Typography>
</Grid>
<Grid size={2}>
<Typography variant='body2' fontWeight='medium'>
Harga Jual
</Typography>
</Grid>
<Grid size={1}>
<Typography variant='body2' fontWeight='medium'>
Default
</Typography>
</Grid>
<Grid size={1}>
<Typography variant='body2' fontWeight='medium'>
Action
</Typography>
</Grid>
</Grid>
{/* Baris Konversi */}
{conversions.map((conversion, index) => (
<Grid container spacing={2} alignItems='center' key={index} className='py-2'>
<Grid size={0.5}>
<Typography variant='body2' color='text.secondary'>
{index + 1}
</Typography>
</Grid>
{/* Satuan */}
<Grid size={1.5}>
<CustomTextField
select
fullWidth
size='small'
value={conversion.satuan}
onChange={e => handleChangeConversion(index, 'satuan', e.target.value)}
>
<MenuItem value='Box'>Box</MenuItem>
<MenuItem value='Kg'>Kg</MenuItem>
<MenuItem value='Liter'>Liter</MenuItem>
<MenuItem value='Pack'>Pack</MenuItem>
<MenuItem value='Pcs'>Pcs</MenuItem>
</CustomTextField>
</Grid>
{/* Tanda sama dengan */}
<Grid size={1} className='text-center'>
<Typography variant='h6'>=</Typography>
</Grid>
{/* Quantity */}
<Grid size={1.5}>
<CustomTextField
fullWidth
size='small'
type='number'
value={conversion.quantity}
onChange={e => handleChangeConversion(index, 'quantity', parseInt(e.target.value) || 0)}
/>
</Grid>
{/* Unit */}
<Grid size={1.5}>
<CustomTextField
select
fullWidth
size='small'
value={conversion.unit}
onChange={e => handleChangeConversion(index, 'unit', e.target.value)}
>
<MenuItem value='Pcs'>Pcs</MenuItem>
<MenuItem value='Kg'>Kg</MenuItem>
<MenuItem value='Gram'>Gram</MenuItem>
<MenuItem value='Liter'>Liter</MenuItem>
<MenuItem value='ML'>ML</MenuItem>
</CustomTextField>
</Grid>
{/* Harga Beli */}
<Grid size={2}>
<CustomTextField
fullWidth
size='small'
value={formatNumber(conversion.hargaBeli)}
onChange={e => handleChangeConversion(index, 'hargaBeli', parseNumber(e.target.value))}
/>
</Grid>
{/* Harga Jual */}
<Grid size={2}>
<CustomTextField
fullWidth
size='small'
value={formatNumber(conversion.hargaJual)}
onChange={e => handleChangeConversion(index, 'hargaJual', parseNumber(e.target.value))}
/>
</Grid>
{/* Default Star */}
<Grid size={1} className='text-center'>
<IconButton
size='small'
onClick={() => handleToggleDefault(index)}
sx={{
color: conversion.isDefault ? 'warning.main' : 'grey.400'
}}
>
<i className={conversion.isDefault ? 'tabler-star-filled' : 'tabler-star'} />
</IconButton>
</Grid>
{/* Delete Button */}
<Grid size={1} className='text-center'>
{conversions.length > 1 && (
<IconButton
size='small'
onClick={() => handleHapusBaris(index)}
sx={{
color: 'error.main',
border: 1,
borderColor: 'error.main',
'&:hover': {
backgroundColor: 'error.light',
borderColor: 'error.main'
}
}}
>
<i className='tabler-trash' />
</IconButton>
)}
</Grid>
</Grid>
))}
{/* Tambah Baris Button */}
<div className='flex items-center justify-start'>
<Button
variant='outlined'
startIcon={<i className='tabler-plus' />}
onClick={handleTambahBaris}
sx={{
color: 'primary.main',
borderColor: 'primary.main',
'&:hover': {
backgroundColor: 'primary.light',
borderColor: 'primary.main'
}
}}
>
Tambah baris
</Button>
</div>
</div>
</form>
</Box>
{/* Sticky Footer */}
<Box
sx={{
position: 'sticky',
bottom: 0,
zIndex: 10,
backgroundColor: 'background.paper',
borderTop: 1,
borderColor: 'divider',
p: 3
}}
>
<div className='flex items-center gap-4'>
<Button variant='contained' type='submit' form='unit-conversion-form'>
Simpan
</Button>
<Button variant='tonal' color='error' onClick={() => handleReset()}>
Batal
</Button>
</div>
</Box>
</Drawer>
)
}
export default IngedientUnitConversionDrawer

View File

@ -1,8 +1,30 @@
import React from 'react'
'use client'
import React, { useState } from 'react'
import { Card, CardContent, CardHeader, Typography, Button, Box, Stack } from '@mui/material'
import IngedientUnitConversionDrawer from './IngedientUnitConversionDrawer' // Sesuaikan dengan path file Anda
const IngredientDetailUnit = () => {
// State untuk mengontrol drawer
const [openConversionDrawer, setOpenConversionDrawer] = useState(false)
// Function untuk membuka drawer
const handleOpenConversionDrawer = () => {
setOpenConversionDrawer(true)
}
// Function untuk menutup drawer
const handleCloseConversionDrawer = () => {
setOpenConversionDrawer(false)
}
// Function untuk handle data dari drawer (opsional)
const handleSetConversionData = (data: any) => {
console.log('Conversion data received:', data)
// Anda bisa menambahkan logic untuk mengupdate state atau API call di sini
}
return (
<>
<Card sx={{ maxWidth: 400 }}>
<CardHeader title='Satuan' />
<CardContent>
@ -21,6 +43,7 @@ const IngredientDetailUnit = () => {
variant='contained'
fullWidth
startIcon={<span style={{ fontSize: '18px' }}>+</span>}
onClick={handleOpenConversionDrawer}
sx={{
py: 1.5,
borderRadius: 2,
@ -32,6 +55,14 @@ const IngredientDetailUnit = () => {
</Button>
</CardContent>
</Card>
{/* Ingredient Unit Conversion Drawer */}
<IngedientUnitConversionDrawer
open={openConversionDrawer}
handleClose={handleCloseConversionDrawer}
setData={handleSetConversionData}
/>
</>
)
}