Ingredient Detail Stock Adjustment Drawer
This commit is contained in:
parent
5a77d3c2ea
commit
b3a41fe0e0
@ -0,0 +1,454 @@
|
||||
'use client'
|
||||
// React Imports
|
||||
import { useEffect, 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 Grid from '@mui/material/Grid2'
|
||||
import Box from '@mui/material/Box'
|
||||
import Radio from '@mui/material/Radio'
|
||||
import RadioGroup from '@mui/material/RadioGroup'
|
||||
import FormControlLabel from '@mui/material/FormControlLabel'
|
||||
import FormControl from '@mui/material/FormControl'
|
||||
|
||||
// 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 StockAdjustmentType = {
|
||||
tipeStok: 'perhitungan' | 'stokMasukKeluar'
|
||||
gudang: string
|
||||
tanggal: string
|
||||
akun: string
|
||||
nomor: string
|
||||
referensi: string
|
||||
tag: string
|
||||
qtyTercatat: number
|
||||
satuan: string
|
||||
qtyAktual: number
|
||||
selisih: number
|
||||
hargaRataRata: number
|
||||
}
|
||||
|
||||
type FormValidateType = StockAdjustmentType
|
||||
|
||||
// Vars
|
||||
const initialData: StockAdjustmentType = {
|
||||
tipeStok: 'perhitungan',
|
||||
gudang: '',
|
||||
tanggal: '11/09/2025',
|
||||
akun: '8-80100 Penyesuaian Persediaan',
|
||||
nomor: 'SA/00007',
|
||||
referensi: '',
|
||||
tag: '',
|
||||
qtyTercatat: 0,
|
||||
satuan: 'Pcs',
|
||||
qtyAktual: 0,
|
||||
selisih: 0,
|
||||
hargaRataRata: 0
|
||||
}
|
||||
|
||||
const IngredientDetailStockAdjustmentDrawer = (props: Props) => {
|
||||
// Props
|
||||
const { open, handleClose, setData } = props
|
||||
|
||||
// States
|
||||
const [formData, setFormData] = useState<StockAdjustmentType>(initialData)
|
||||
|
||||
// Hooks
|
||||
const {
|
||||
control,
|
||||
reset: resetForm,
|
||||
handleSubmit,
|
||||
watch,
|
||||
setValue,
|
||||
formState: { errors }
|
||||
} = useForm<FormValidateType>({
|
||||
defaultValues: initialData
|
||||
})
|
||||
|
||||
// Watch values untuk kalkulasi otomatis
|
||||
const qtyTercatat = watch('qtyTercatat')
|
||||
const qtyAktual = watch('qtyAktual')
|
||||
|
||||
// Kalkulasi selisih otomatis
|
||||
useEffect(() => {
|
||||
const selisih = qtyAktual - qtyTercatat
|
||||
setValue('selisih', selisih)
|
||||
}, [qtyTercatat, qtyAktual, setValue])
|
||||
|
||||
const onSubmit = (data: FormValidateType) => {
|
||||
console.log('Stock adjustment data:', data)
|
||||
if (setData) {
|
||||
setData(data)
|
||||
}
|
||||
handleClose()
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
handleClose()
|
||||
resetForm(initialData)
|
||||
setFormData(initialData)
|
||||
}
|
||||
|
||||
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: 400, sm: 600 },
|
||||
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'>Penyesuaian Stok</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='stock-adjustment-form' onSubmit={handleSubmit(data => onSubmit(data))}>
|
||||
<div className='flex flex-col gap-6 p-6'>
|
||||
{/* Tipe Penyesuaian Stok */}
|
||||
<div>
|
||||
<Typography variant='body2' className='mb-3' sx={{ color: 'error.main' }}>
|
||||
* Tipe penyesuaian stok
|
||||
</Typography>
|
||||
<Controller
|
||||
name='tipeStok'
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<FormControl component='fieldset'>
|
||||
<RadioGroup {...field} row sx={{ gap: 4 }}>
|
||||
<FormControlLabel
|
||||
value='perhitungan'
|
||||
control={<Radio />}
|
||||
label={
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<Typography>Perhitungan Stok</Typography>
|
||||
<IconButton size='small' sx={{ color: 'text.secondary' }}>
|
||||
<i className='tabler-help-circle' style={{ fontSize: '16px' }} />
|
||||
</IconButton>
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
<FormControlLabel
|
||||
value='stokMasukKeluar'
|
||||
control={<Radio />}
|
||||
label={
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<Typography>Stok Masuk / Keluar</Typography>
|
||||
<IconButton size='small' sx={{ color: 'text.secondary' }}>
|
||||
<i className='tabler-help-circle' style={{ fontSize: '16px' }} />
|
||||
</IconButton>
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Gudang dan Tanggal */}
|
||||
<Grid container spacing={4}>
|
||||
<Grid size={6}>
|
||||
<Typography variant='body2' className='mb-2' sx={{ color: 'error.main' }}>
|
||||
* Gudang
|
||||
</Typography>
|
||||
<Controller
|
||||
name='gudang'
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
select
|
||||
fullWidth
|
||||
placeholder='Pilih gudang'
|
||||
{...(errors.gudang && { error: true, helperText: 'Field ini wajib diisi.' })}
|
||||
>
|
||||
<MenuItem value=''>Pilih gudang</MenuItem>
|
||||
<MenuItem value='Gudang Utama'>Gudang Utama</MenuItem>
|
||||
<MenuItem value='Gudang Cabang'>Gudang Cabang</MenuItem>
|
||||
<MenuItem value='Gudang Produksi'>Gudang Produksi</MenuItem>
|
||||
</CustomTextField>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={6}>
|
||||
<Typography variant='body2' className='mb-2' sx={{ color: 'error.main' }}>
|
||||
* Tanggal
|
||||
</Typography>
|
||||
<Controller
|
||||
name='tanggal'
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
fullWidth
|
||||
type='date'
|
||||
{...(errors.tanggal && { error: true, helperText: 'Field ini wajib diisi.' })}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* Akun dan Nomor */}
|
||||
<Grid container spacing={4}>
|
||||
<Grid size={6}>
|
||||
<Typography variant='body2' className='mb-2' sx={{ color: 'error.main' }}>
|
||||
* Akun
|
||||
<IconButton size='small' sx={{ color: 'text.secondary', ml: 1 }}>
|
||||
<i className='tabler-help-circle' style={{ fontSize: '16px' }} />
|
||||
</IconButton>
|
||||
</Typography>
|
||||
<Controller
|
||||
name='akun'
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
select
|
||||
fullWidth
|
||||
{...(errors.akun && { error: true, helperText: 'Field ini wajib diisi.' })}
|
||||
>
|
||||
<MenuItem value='8-80100 Penyesuaian Persediaan'>8-80100 Penyesuaian Persediaan</MenuItem>
|
||||
<MenuItem value='8-80200 Penyesuaian Stok Rusak'>8-80200 Penyesuaian Stok Rusak</MenuItem>
|
||||
<MenuItem value='8-80300 Penyesuaian Stok Hilang'>8-80300 Penyesuaian Stok Hilang</MenuItem>
|
||||
</CustomTextField>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={6}>
|
||||
<Typography variant='body2' className='mb-2'>
|
||||
Nomor
|
||||
<IconButton size='small' sx={{ color: 'text.secondary', ml: 1 }}>
|
||||
<i className='tabler-help-circle' style={{ fontSize: '16px' }} />
|
||||
</IconButton>
|
||||
</Typography>
|
||||
<Controller
|
||||
name='nomor'
|
||||
control={control}
|
||||
render={({ field }) => <CustomTextField {...field} fullWidth placeholder='SA/00007' />}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* Referensi dan Tag */}
|
||||
<Grid container spacing={4}>
|
||||
<Grid size={6}>
|
||||
<Typography variant='body2' className='mb-2'>
|
||||
Referensi
|
||||
<IconButton size='small' sx={{ color: 'text.secondary', ml: 1 }}>
|
||||
<i className='tabler-help-circle' style={{ fontSize: '16px' }} />
|
||||
</IconButton>
|
||||
</Typography>
|
||||
<Controller
|
||||
name='referensi'
|
||||
control={control}
|
||||
render={({ field }) => <CustomTextField {...field} fullWidth placeholder='Referensi' />}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={6}>
|
||||
<Typography variant='body2' className='mb-2'>
|
||||
Tag
|
||||
<IconButton size='small' sx={{ color: 'text.secondary', ml: 1 }}>
|
||||
<i className='tabler-help-circle' style={{ fontSize: '16px' }} />
|
||||
</IconButton>
|
||||
</Typography>
|
||||
<Controller
|
||||
name='tag'
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<CustomTextField {...field} select fullWidth placeholder='Pilih Tag'>
|
||||
<MenuItem value=''>Pilih Tag</MenuItem>
|
||||
<MenuItem value='Urgent'>Urgent</MenuItem>
|
||||
<MenuItem value='Regular'>Regular</MenuItem>
|
||||
<MenuItem value='Maintenance'>Maintenance</MenuItem>
|
||||
</CustomTextField>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* Stock Details Section */}
|
||||
<Box sx={{ mt: 4 }}>
|
||||
<Grid container spacing={2} alignItems='end'>
|
||||
{/* Qty Tercatat */}
|
||||
<Grid size={2}>
|
||||
<Typography variant='body2' className='mb-2 font-medium'>
|
||||
Qty Tercatat
|
||||
</Typography>
|
||||
<Controller
|
||||
name='qtyTercatat'
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
fullWidth
|
||||
type='number'
|
||||
onChange={e => field.onChange(parseInt(e.target.value) || 0)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
{/* Satuan */}
|
||||
<Grid size={2}>
|
||||
<Typography variant='body2' className='mb-2 font-medium'>
|
||||
Satuan
|
||||
</Typography>
|
||||
<Controller
|
||||
name='satuan'
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<CustomTextField {...field} select fullWidth>
|
||||
<MenuItem value='Pcs'>Pcs</MenuItem>
|
||||
<MenuItem value='Kg'>Kg</MenuItem>
|
||||
<MenuItem value='Liter'>Liter</MenuItem>
|
||||
<MenuItem value='Box'>Box</MenuItem>
|
||||
</CustomTextField>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
{/* Qty Aktual */}
|
||||
<Grid size={2}>
|
||||
<Typography variant='body2' className='mb-2 font-medium'>
|
||||
Qty Aktual
|
||||
</Typography>
|
||||
<Controller
|
||||
name='qtyAktual'
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
fullWidth
|
||||
type='number'
|
||||
onChange={e => field.onChange(parseInt(e.target.value) || 0)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
{/* Selisih */}
|
||||
<Grid size={2}>
|
||||
<Typography variant='body2' className='mb-2 font-medium'>
|
||||
Selisih
|
||||
</Typography>
|
||||
<Controller
|
||||
name='selisih'
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
fullWidth
|
||||
type='number'
|
||||
disabled
|
||||
sx={{
|
||||
'& .MuiInputBase-input.Mui-disabled': {
|
||||
WebkitTextFillColor: 'text.primary',
|
||||
backgroundColor: 'grey.100'
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
{/* Harga Rata-rata */}
|
||||
<Grid size={4}>
|
||||
<Typography variant='body2' className='mb-2 font-medium'>
|
||||
Harga Rata-rata
|
||||
</Typography>
|
||||
<Controller
|
||||
name='hargaRataRata'
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
fullWidth
|
||||
value={formatNumber(field.value)}
|
||||
onChange={e => field.onChange(parseNumber(e.target.value))}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
</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='stock-adjustment-form'>
|
||||
Simpan
|
||||
</Button>
|
||||
<Button variant='tonal' color='error' onClick={() => handleReset()}>
|
||||
Batal
|
||||
</Button>
|
||||
</div>
|
||||
</Box>
|
||||
</Drawer>
|
||||
)
|
||||
}
|
||||
|
||||
export default IngredientDetailStockAdjustmentDrawer
|
||||
@ -1,17 +1,67 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import IngredientDetailInfo from './IngredientDetailInfo'
|
||||
import IngredientDetailUnit from './IngredientDetailUnit'
|
||||
import IngredientDetailStockAdjustmentDrawer from './IngredientDetailStockAdjustmentDrawer' // Sesuaikan dengan path file Anda
|
||||
import { Button } from '@mui/material'
|
||||
|
||||
const IngredientDetail = () => {
|
||||
// State untuk mengontrol stock adjustment drawer
|
||||
const [openStockAdjustmentDrawer, setOpenStockAdjustmentDrawer] = useState(false)
|
||||
|
||||
// Function untuk membuka stock adjustment drawer
|
||||
const handleOpenStockAdjustmentDrawer = () => {
|
||||
setOpenStockAdjustmentDrawer(true)
|
||||
}
|
||||
|
||||
// Function untuk menutup stock adjustment drawer
|
||||
const handleCloseStockAdjustmentDrawer = () => {
|
||||
setOpenStockAdjustmentDrawer(false)
|
||||
}
|
||||
|
||||
// Function untuk handle data dari stock adjustment drawer
|
||||
const handleSetStockAdjustmentData = (data: any) => {
|
||||
console.log('Stock adjustment data received:', data)
|
||||
// Anda bisa menambahkan logic untuk mengupdate state atau API call di sini
|
||||
// Misalnya: update stock data, refresh ingredient data, dll.
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12, lg: 8, md: 7 }}>
|
||||
<IngredientDetailInfo />
|
||||
<>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12, lg: 8, md: 7 }}>
|
||||
<IngredientDetailInfo />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, lg: 4, md: 5 }}>
|
||||
<Button
|
||||
variant='contained'
|
||||
fullWidth
|
||||
startIcon={<span style={{ fontSize: '18px' }}>+</span>}
|
||||
onClick={handleOpenStockAdjustmentDrawer}
|
||||
className='mb-4'
|
||||
sx={{
|
||||
py: 1.5,
|
||||
borderRadius: 2,
|
||||
textTransform: 'none',
|
||||
fontSize: '16px',
|
||||
mb: 3 // Menambahkan margin bottom untuk spacing
|
||||
}}
|
||||
>
|
||||
Penyesuaian Stok
|
||||
</Button>
|
||||
<IngredientDetailUnit />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, lg: 4, md: 5 }}>
|
||||
<IngredientDetailUnit />
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* Stock Adjustment Drawer */}
|
||||
<IngredientDetailStockAdjustmentDrawer
|
||||
open={openStockAdjustmentDrawer}
|
||||
handleClose={handleCloseStockAdjustmentDrawer}
|
||||
setData={handleSetStockAdjustmentData}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user