commit
32a36cceff
444 files changed
+67186
No files matched your search
@@ -0,0 +1,240 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { FeedSackColumnConfig, FEED_SACK_COLUMN_COLORS } from '../../types/counting.ts';
|
||||
|
||||
interface ColumnConfigModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSave: (columns: FeedSackColumnConfig[]) => void;
|
||||
columns: FeedSackColumnConfig[];
|
||||
availableCameras: string[];
|
||||
}
|
||||
|
||||
const ColumnConfigModal: React.FC<ColumnConfigModalProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
onSave,
|
||||
columns,
|
||||
availableCameras,
|
||||
}) => {
|
||||
const [editColumns, setEditColumns] = useState<FeedSackColumnConfig[]>(columns);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
setEditColumns(columns);
|
||||
}
|
||||
}, [columns, isOpen]);
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
const handleAddColumn = () => {
|
||||
const usageCount = editColumns.filter((column) => column.type === 'usage').length;
|
||||
const nextColor = usageCount % 2 === 0 ? '#eab308' : '#a855f7';
|
||||
const usedCameras = new Set(editColumns.map((c) => c.cameraName));
|
||||
const availableCamera =
|
||||
availableCameras.find((c) => !usedCameras.has(c)) || availableCameras[0] || '';
|
||||
|
||||
const newColumn: FeedSackColumnConfig = {
|
||||
id: `col-${Date.now()}`,
|
||||
label: '',
|
||||
cameraName: availableCamera,
|
||||
color: nextColor,
|
||||
type: 'usage',
|
||||
kandangId: null,
|
||||
};
|
||||
|
||||
setEditColumns([...editColumns, newColumn]);
|
||||
};
|
||||
|
||||
const handleRemoveColumn = (id: string) => {
|
||||
setEditColumns(editColumns.filter((c) => c.id !== id));
|
||||
};
|
||||
|
||||
const handleUpdateColumn = (id: string, field: keyof FeedSackColumnConfig, value: string) => {
|
||||
setEditColumns(editColumns.map((c) => (c.id === id ? { ...c, [field]: value } : c)));
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
const validColumns = editColumns.filter((c) => c.cameraName && c.label.trim() && c.type);
|
||||
|
||||
const hasInbound = validColumns.some((c) => c.type === 'inbound');
|
||||
if (!hasInbound) {
|
||||
alert('Minimal harus ada satu kolom "Karung Masuk"');
|
||||
return;
|
||||
}
|
||||
|
||||
onSave(validColumns);
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="fixed inset-0 bg-black bg-opacity-60 flex justify-center items-center z-50 p-4"
|
||||
onClick={onClose}
|
||||
>
|
||||
<div
|
||||
className="bg-white rounded-xl shadow-2xl w-full max-w-2xl max-h-[80vh] flex flex-col"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="p-6 overflow-y-auto flex-1">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h2 className="text-2xl font-bold text-gray-800">Konfigurasi Kolom</h2>
|
||||
<button onClick={onClose} className="text-gray-400 hover:text-gray-600 text-2xl">
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
{editColumns.map((col, index) => (
|
||||
<div key={col.id} className="border border-gray-200 rounded-lg p-4 bg-gray-50">
|
||||
<div className="flex justify-between items-center mb-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm font-semibold text-gray-600">Kolom {index + 1}</span>
|
||||
<span
|
||||
className={`text-xs px-2 py-0.5 rounded-full font-medium ${
|
||||
col.type === 'inbound'
|
||||
? 'bg-green-100 text-green-700'
|
||||
: col.type === 'usage'
|
||||
? 'bg-yellow-100 text-yellow-700'
|
||||
: 'bg-red-100 text-red-700'
|
||||
}`}
|
||||
>
|
||||
{col.type === 'inbound'
|
||||
? 'Masuk'
|
||||
: col.type === 'usage'
|
||||
? 'Dituang'
|
||||
: 'Keluar'}
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => handleRemoveColumn(col.id)}
|
||||
className="text-red-500 hover:text-red-700 text-sm font-medium"
|
||||
>
|
||||
<i className="fa-solid fa-trash mr-1"></i>Hapus
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="mb-3">
|
||||
<label className="block text-xs font-medium text-gray-700 mb-1">Label</label>
|
||||
<input
|
||||
type="text"
|
||||
value={col.label}
|
||||
onChange={(e) => handleUpdateColumn(col.id, 'label', e.target.value)}
|
||||
placeholder="e.g., Karung Masuk"
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-3">
|
||||
<label className="block text-xs font-medium text-gray-700 mb-1">Kamera</label>
|
||||
<select
|
||||
value={col.cameraName}
|
||||
onChange={(e) => handleUpdateColumn(col.id, 'cameraName', e.target.value)}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white"
|
||||
>
|
||||
<option value="">-- Pilih Kamera --</option>
|
||||
{availableCameras.map((cam) => (
|
||||
<option key={cam} value={cam}>
|
||||
{cam}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="mb-3">
|
||||
<label className="block text-xs font-medium text-gray-700 mb-1">Warna</label>
|
||||
<div className="flex gap-2">
|
||||
{FEED_SACK_COLUMN_COLORS.map((color) => (
|
||||
<button
|
||||
key={color}
|
||||
type="button"
|
||||
onClick={() => handleUpdateColumn(col.id, 'color', color)}
|
||||
className={`w-7 h-7 rounded-full border-2 transition-all ${
|
||||
col.color === color
|
||||
? 'border-gray-800 scale-110'
|
||||
: 'border-transparent hover:border-gray-300'
|
||||
}`}
|
||||
style={{ backgroundColor: color }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-gray-700 mb-1">Tipe Kolom</label>
|
||||
<div className="flex gap-3">
|
||||
<label className="flex items-center gap-2 cursor-pointer">
|
||||
<input
|
||||
type="radio"
|
||||
name={`type-${col.id}`}
|
||||
value="inbound"
|
||||
checked={col.type === 'inbound'}
|
||||
onChange={() => handleUpdateColumn(col.id, 'type', 'inbound')}
|
||||
className="w-4 h-4 text-green-600 focus:ring-green-500"
|
||||
/>
|
||||
<span className="text-sm text-gray-700">
|
||||
<i className="fa-solid fa-arrow-down text-green-600 mr-1"></i>
|
||||
Karung Masuk
|
||||
</span>
|
||||
</label>
|
||||
<label className="flex items-center gap-2 cursor-pointer">
|
||||
<input
|
||||
type="radio"
|
||||
name={`type-${col.id}`}
|
||||
value="usage"
|
||||
checked={col.type === 'usage'}
|
||||
onChange={() => handleUpdateColumn(col.id, 'type', 'usage')}
|
||||
className="w-4 h-4 text-yellow-600 focus:ring-yellow-500"
|
||||
/>
|
||||
<span className="text-sm text-gray-700">
|
||||
<i className="fa-solid fa-arrow-up text-yellow-600 mr-1"></i>
|
||||
Karung Dituang
|
||||
</span>
|
||||
</label>
|
||||
<label className="flex items-center gap-2 cursor-pointer">
|
||||
<input
|
||||
type="radio"
|
||||
name={`type-${col.id}`}
|
||||
value="outbound"
|
||||
checked={col.type === 'outbound'}
|
||||
onChange={() => handleUpdateColumn(col.id, 'type', 'outbound')}
|
||||
className="w-4 h-4 text-red-600 focus:ring-red-500"
|
||||
/>
|
||||
<span className="text-sm text-gray-700">
|
||||
<i className="fa-solid fa-arrow-right text-red-600 mr-1"></i>
|
||||
Karung Keluar
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={handleAddColumn}
|
||||
className="mt-4 w-full py-2 border-2 border-dashed border-gray-300 rounded-lg text-gray-500 hover:text-gray-700 hover:border-gray-400 transition-colors text-sm font-medium"
|
||||
>
|
||||
<i className="fa-solid fa-plus mr-2"></i>Tambah Kolom
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="bg-gray-50 px-6 py-4 rounded-b-xl flex justify-end space-x-3 border-t border-gray-200">
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="bg-gray-200 text-gray-700 px-4 py-2 rounded-lg hover:bg-gray-300 transition duration-300 font-medium"
|
||||
>
|
||||
Batal
|
||||
</button>
|
||||
<button
|
||||
onClick={handleSave}
|
||||
className="bg-red-600 text-white px-4 py-2 rounded-lg hover:bg-red-700 transition duration-300 font-medium shadow-sm"
|
||||
>
|
||||
Simpan
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ColumnConfigModal;
|
||||
Reference in new issue
Block a user