2025-09-15 21:11:40 +00:00
|
|
|
|
import React from 'react'
|
|
|
|
|
|
import { useFormik } from 'formik'
|
|
|
|
|
|
import * as Yup from 'yup'
|
|
|
|
|
|
import { FaSave, FaTimes } from 'react-icons/fa'
|
|
|
|
|
|
import { QualityStatusEnum, MmLotNumber, MmUnit, MmMaterial } from '../../../types/mm'
|
|
|
|
|
|
import { BusinessParty } from '../../../types/common'
|
2025-09-17 09:46:58 +00:00
|
|
|
|
import { getQualityStatusText } from '@/utils/erp'
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
const validationSchema = Yup.object({
|
2025-09-15 21:11:40 +00:00
|
|
|
|
materialId: Yup.string().required('Malzeme seçimi zorunlu'),
|
|
|
|
|
|
lotNumber: Yup.string().required('Lot numarası zorunlu'),
|
2025-09-15 09:31:47 +00:00
|
|
|
|
quantity: Yup.number().min(0, "Miktar 0'dan büyük olmalıdır"),
|
2025-09-15 21:11:40 +00:00
|
|
|
|
})
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
interface LotFormProps {
|
2025-09-15 21:11:40 +00:00
|
|
|
|
isOpen: boolean
|
|
|
|
|
|
onClose: () => void
|
|
|
|
|
|
onSave: (lot: MmLotNumber) => void
|
|
|
|
|
|
onUpdate?: (lot: MmLotNumber) => void
|
|
|
|
|
|
materials: MmMaterial[]
|
|
|
|
|
|
suppliers?: BusinessParty[]
|
|
|
|
|
|
units?: MmUnit[]
|
|
|
|
|
|
initial?: MmLotNumber | null
|
|
|
|
|
|
mode?: 'create' | 'edit' | 'view'
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const LotForm: React.FC<LotFormProps> = ({
|
|
|
|
|
|
isOpen,
|
|
|
|
|
|
onClose,
|
|
|
|
|
|
onSave,
|
|
|
|
|
|
onUpdate,
|
|
|
|
|
|
materials,
|
|
|
|
|
|
suppliers = [],
|
|
|
|
|
|
units = [],
|
|
|
|
|
|
initial = null,
|
2025-09-15 21:11:40 +00:00
|
|
|
|
mode = 'create',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}) => {
|
|
|
|
|
|
const formik = useFormik({
|
|
|
|
|
|
initialValues: {
|
2025-09-15 21:11:40 +00:00
|
|
|
|
materialId: '',
|
|
|
|
|
|
lotNumber: '',
|
|
|
|
|
|
productionDate: '',
|
|
|
|
|
|
expiryDate: '',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
quantity: 0,
|
2025-09-15 21:11:40 +00:00
|
|
|
|
unitId: 'KG',
|
|
|
|
|
|
supplierId: suppliers.length ? suppliers[0].id : '',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
qualityStatus: QualityStatusEnum.Pending,
|
|
|
|
|
|
isActive: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
validationSchema,
|
|
|
|
|
|
onSubmit: async (values) => {
|
|
|
|
|
|
const newLot: MmLotNumber = {
|
|
|
|
|
|
id: (initial && initial.id) || Date.now().toString(),
|
|
|
|
|
|
materialId: values.materialId,
|
|
|
|
|
|
lotNumber: values.lotNumber,
|
2025-09-15 21:11:40 +00:00
|
|
|
|
productionDate: values.productionDate ? new Date(values.productionDate) : new Date(),
|
2025-09-15 09:31:47 +00:00
|
|
|
|
expiryDate: values.expiryDate ? new Date(values.expiryDate) : undefined,
|
|
|
|
|
|
quantity: Number(values.quantity),
|
|
|
|
|
|
unitId: values.unitId,
|
|
|
|
|
|
supplierId: values.supplierId || undefined,
|
|
|
|
|
|
qualityStatus: values.qualityStatus,
|
|
|
|
|
|
isActive: !!values.isActive,
|
2025-09-15 21:11:40 +00:00
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// simulate API
|
2025-09-15 21:11:40 +00:00
|
|
|
|
await new Promise((r) => setTimeout(r, 300))
|
|
|
|
|
|
if (mode === 'edit' && onUpdate) {
|
|
|
|
|
|
onUpdate(newLot)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
} else {
|
2025-09-15 21:11:40 +00:00
|
|
|
|
onSave(newLot)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
2025-09-15 21:11:40 +00:00
|
|
|
|
onClose()
|
2025-09-15 09:31:47 +00:00
|
|
|
|
},
|
2025-09-15 21:11:40 +00:00
|
|
|
|
})
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// sync initial values when editing/viewing
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
|
|
if (initial) {
|
2025-09-15 21:11:40 +00:00
|
|
|
|
const src = initial
|
2025-09-15 09:31:47 +00:00
|
|
|
|
formik.setValues({
|
2025-09-15 21:11:40 +00:00
|
|
|
|
materialId: src.materialId || '',
|
|
|
|
|
|
lotNumber: src.lotNumber || '',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
productionDate: src.productionDate
|
|
|
|
|
|
? new Date(src.productionDate).toISOString().slice(0, 10)
|
2025-09-15 21:11:40 +00:00
|
|
|
|
: '',
|
|
|
|
|
|
expiryDate: src.expiryDate ? new Date(src.expiryDate).toISOString().slice(0, 10) : '',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
quantity: src.quantity || 0,
|
2025-09-15 21:11:40 +00:00
|
|
|
|
unitId: src.unitId || (units.length ? units[0].id : ''),
|
|
|
|
|
|
supplierId: src.supplierId || (suppliers.length ? suppliers[0].id : ''),
|
2025-09-15 09:31:47 +00:00
|
|
|
|
qualityStatus: src.qualityStatus || QualityStatusEnum.Pending,
|
|
|
|
|
|
isActive: !!src.isActive,
|
2025-09-15 21:11:40 +00:00
|
|
|
|
})
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
2025-09-15 21:11:40 +00:00
|
|
|
|
}, [initial])
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
2025-09-15 21:11:40 +00:00
|
|
|
|
if (!isOpen) return null
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div className="fixed inset-0 z-50 flex items-center justify-center">
|
|
|
|
|
|
<div className="absolute inset-0 bg-black opacity-40" onClick={onClose} />
|
|
|
|
|
|
<div className="relative bg-white rounded-lg shadow-lg w-full max-w-2xl mx-4">
|
|
|
|
|
|
<form onSubmit={formik.handleSubmit} className="space-y-2 p-3">
|
|
|
|
|
|
<div className="flex items-center justify-between p-2 border-b">
|
|
|
|
|
|
<div>
|
2025-09-15 21:02:48 +00:00
|
|
|
|
<h2 className="text-2xl font-bold text-gray-900">
|
2025-09-15 21:11:40 +00:00
|
|
|
|
{mode === 'create'
|
|
|
|
|
|
? 'Yeni Lot Kaydı'
|
|
|
|
|
|
: mode === 'edit'
|
|
|
|
|
|
? 'Lot Düzenle'
|
|
|
|
|
|
: 'Lot Detayı'}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</h2>
|
2025-09-15 21:02:48 +00:00
|
|
|
|
<p className="text-gray-600">
|
2025-09-15 21:11:40 +00:00
|
|
|
|
{mode === 'create'
|
|
|
|
|
|
? 'Lot bilgilerini girin'
|
|
|
|
|
|
: mode === 'edit'
|
|
|
|
|
|
? 'Mevcut lot bilgilerini güncelleyin'
|
|
|
|
|
|
: 'Lot bilgileri (sadece gösterim)'}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="flex items-center space-x-2">
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
onClick={onClose}
|
|
|
|
|
|
className="px-2.5 py-1 text-sm border rounded-md bg-white hover:bg-gray-50"
|
|
|
|
|
|
>
|
|
|
|
|
|
<FaTimes className="inline mr-1 h-3 w-3" /> Kapat
|
|
|
|
|
|
</button>
|
2025-09-15 21:11:40 +00:00
|
|
|
|
{mode !== 'view' && (
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<button
|
|
|
|
|
|
type="submit"
|
|
|
|
|
|
className="px-2.5 py-1 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700 flex items-center"
|
|
|
|
|
|
>
|
|
|
|
|
|
<FaSave className="inline mr-1 h-3 w-3" /> Kaydet
|
|
|
|
|
|
</button>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 p-2 gap-x-3 gap-y-2">
|
|
|
|
|
|
<div>
|
2025-09-15 21:11:40 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700">Malzeme *</label>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<select
|
2025-09-15 21:11:40 +00:00
|
|
|
|
{...formik.getFieldProps('materialId')}
|
|
|
|
|
|
disabled={mode === 'view'}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
className="w-full border rounded-md px-2 py-1 text-sm"
|
|
|
|
|
|
>
|
|
|
|
|
|
<option value="">Seçiniz...</option>
|
|
|
|
|
|
{materials.map((m) => (
|
|
|
|
|
|
<option key={m.id} value={m.id}>
|
|
|
|
|
|
{m.code} - {m.name}
|
|
|
|
|
|
</option>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
2025-09-15 21:11:40 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700">Lot Numarası *</label>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<input
|
|
|
|
|
|
type="text"
|
2025-09-15 21:11:40 +00:00
|
|
|
|
{...formik.getFieldProps('lotNumber')}
|
|
|
|
|
|
disabled={mode === 'view'}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
className="w-full border rounded-md px-2 py-1 text-sm"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
2025-09-15 21:11:40 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700">Üretim Tarihi</label>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<input
|
|
|
|
|
|
type="date"
|
2025-09-15 21:11:40 +00:00
|
|
|
|
{...formik.getFieldProps('productionDate')}
|
|
|
|
|
|
disabled={mode === 'view'}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
className="w-full border rounded-md px-2 py-1 text-sm"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
2025-09-15 21:11:40 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700">Son Kullanma Tarihi</label>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<input
|
|
|
|
|
|
type="date"
|
2025-09-15 21:11:40 +00:00
|
|
|
|
{...formik.getFieldProps('expiryDate')}
|
|
|
|
|
|
disabled={mode === 'view'}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
className="w-full border rounded-md px-2 py-1 text-sm"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
2025-09-15 21:11:40 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700">Miktar</label>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<input
|
|
|
|
|
|
type="number"
|
|
|
|
|
|
step="0.01"
|
2025-09-15 21:11:40 +00:00
|
|
|
|
{...formik.getFieldProps('quantity')}
|
|
|
|
|
|
disabled={mode === 'view'}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
className="w-full border rounded-md px-2 py-1 text-sm"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
2025-09-15 21:11:40 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700">Birim</label>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<select
|
2025-09-15 21:11:40 +00:00
|
|
|
|
{...formik.getFieldProps('unitId')}
|
|
|
|
|
|
disabled={mode === 'view'}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
className="w-full border rounded-md px-2 py-1 text-sm"
|
|
|
|
|
|
>
|
|
|
|
|
|
{units.map((u) => (
|
|
|
|
|
|
<option key={u.id} value={u.id}>
|
|
|
|
|
|
{u.code}
|
|
|
|
|
|
</option>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
2025-09-15 21:11:40 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700">Tedarikçi</label>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<select
|
2025-09-15 21:11:40 +00:00
|
|
|
|
{...formik.getFieldProps('supplierId')}
|
|
|
|
|
|
disabled={mode === 'view'}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
className="w-full border rounded-md px-2 py-1 text-sm"
|
|
|
|
|
|
>
|
|
|
|
|
|
<option value="">Seçiniz...</option>
|
|
|
|
|
|
{suppliers.map((s) => (
|
|
|
|
|
|
<option key={s.id} value={s.id}>
|
|
|
|
|
|
{s.code} - {s.name}
|
|
|
|
|
|
</option>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
2025-09-15 21:11:40 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700">Kalite Durumu</label>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<select
|
2025-09-15 21:11:40 +00:00
|
|
|
|
{...formik.getFieldProps('qualityStatus')}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
className="w-full border rounded-md px-2 py-1 text-sm"
|
|
|
|
|
|
>
|
2025-09-17 09:46:58 +00:00
|
|
|
|
{Object.values(QualityStatusEnum).map((status) => (
|
|
|
|
|
|
<option key={status} value={status}>
|
|
|
|
|
|
{getQualityStatusText(status)}
|
|
|
|
|
|
</option>
|
|
|
|
|
|
))}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</form>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-09-15 21:11:40 +00:00
|
|
|
|
)
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
2025-09-15 21:11:40 +00:00
|
|
|
|
export default LotForm
|