- Boost text contrast in both themes - Strengthen border visibility (subtle borders now visible) - Convert 39 files from hardcoded dark:/light: to CSS vars - Tertiary text now more readable on both backgrounds
134 lines
6.4 KiB
TypeScript
134 lines
6.4 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import { X, ArrowRight, Sprout, Home } from 'lucide-react';
|
|
import { Batch, batchesApi } from '../lib/batchesApi';
|
|
import { Room, roomsApi } from '../lib/roomsApi';
|
|
|
|
interface TransitionModalProps {
|
|
batch: Batch;
|
|
onClose: () => void;
|
|
onSuccess: () => void;
|
|
}
|
|
|
|
const STAGES = [
|
|
{ id: 'CLONE_IN', label: 'Clone In' },
|
|
{ id: 'VEGETATIVE', label: 'Vegetative' },
|
|
{ id: 'FLOWERING', label: 'Flowering' },
|
|
{ id: 'HARVEST', label: 'Harvest' },
|
|
{ id: 'DRYING', label: 'Drying' },
|
|
{ id: 'CURING', label: 'Curing' },
|
|
{ id: 'FINISHED', label: 'Finished' }
|
|
];
|
|
|
|
export default function BatchTransitionModal({ batch, onClose, onSuccess }: TransitionModalProps) {
|
|
const [rooms, setRooms] = useState<Room[]>([]);
|
|
const [targetStage, setTargetStage] = useState(batch.stage);
|
|
const [targetRoomId, setTargetRoomId] = useState(batch.roomId || '');
|
|
const [metadata, setMetadata] = useState({
|
|
plantCount: batch.plantCount,
|
|
notes: ''
|
|
});
|
|
|
|
useEffect(() => {
|
|
roomsApi.getAll().then(setRooms);
|
|
|
|
// Auto-select next stage
|
|
const currentIndex = STAGES.findIndex(s => s.id === batch.stage);
|
|
if (currentIndex !== -1 && currentIndex < STAGES.length - 1) {
|
|
setTargetStage(STAGES[currentIndex + 1].id as any);
|
|
}
|
|
}, [batch]);
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
try {
|
|
await batchesApi.update(batch.id, {
|
|
stage: targetStage,
|
|
roomId: targetRoomId,
|
|
plantCount: metadata.plantCount
|
|
});
|
|
onSuccess();
|
|
onClose();
|
|
} catch (error) {
|
|
console.error('Failed to transition batch', error);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm animate-fade-in">
|
|
<div className="bg-[var(--color-bg-elevated)] w-full max-w-md rounded-2xl shadow-2xl overflow-hidden animate-scale-in">
|
|
<div className="p-6 border-b border-[var(--color-border-default)] flex justify-between items-center">
|
|
<h2 className="text-xl font-bold dark:text-white">Transition Batch</h2>
|
|
<button onClick={onClose} className="text-[var(--color-text-tertiary)] hover:text-slate-700 dark:hover:text-slate-300">
|
|
<X size={24} />
|
|
</button>
|
|
</div>
|
|
|
|
<form onSubmit={handleSubmit} className="p-6 space-y-6">
|
|
<div className="bg-[var(--color-bg-tertiary)] p-4 rounded-xl border border-[var(--color-border-default)]">
|
|
<div className="flex items-center gap-3 mb-2">
|
|
<Sprout className="text-[var(--color-primary)] dark:text-emerald-400" size={20} />
|
|
<span className="font-semibold text-[var(--color-text-primary)]">{batch.name}</span>
|
|
</div>
|
|
<div className="flex items-center gap-2 text-sm text-[var(--color-text-tertiary)]">
|
|
<span>{STAGES.find(s => s.id === batch.stage)?.label}</span>
|
|
<ArrowRight size={14} />
|
|
<span className="text-[var(--color-primary)] font-medium">{STAGES.find(s => s.id === targetStage)?.label}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-[var(--color-text-secondary)] mb-2">New Stage</label>
|
|
<select
|
|
value={targetStage}
|
|
onChange={(e) => setTargetStage(e.target.value as any)}
|
|
className="w-full p-3 rounded-lg bg-[var(--color-bg-elevated)] border border-[var(--color-border-default)] dark:text-white"
|
|
>
|
|
{STAGES.map(stage => (
|
|
<option key={stage.id} value={stage.id}>{stage.label}</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-[var(--color-text-secondary)] mb-2">
|
|
<div className="flex items-center gap-2">
|
|
<Home size={16} />
|
|
Move to Room
|
|
</div>
|
|
</label>
|
|
<select
|
|
value={targetRoomId}
|
|
onChange={(e) => setTargetRoomId(e.target.value)}
|
|
className="w-full p-3 rounded-lg bg-[var(--color-bg-elevated)] border border-[var(--color-border-default)] dark:text-white"
|
|
>
|
|
<option value="">Keep current room ({batch.room?.name || 'Unassigned'})</option>
|
|
{rooms.map(room => (
|
|
<option key={room.id} value={room.id}>{room.name} ({room.type})</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<label className="block text-sm font-medium text-[var(--color-text-secondary)] mb-2">Plant Count</label>
|
|
<input
|
|
type="number"
|
|
value={metadata.plantCount}
|
|
onChange={(e) => setMetadata({ ...metadata, plantCount: parseInt(e.target.value) || 0 })}
|
|
className="w-full p-3 rounded-lg bg-[var(--color-bg-elevated)] border border-[var(--color-border-default)] dark:text-white"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<button
|
|
type="submit"
|
|
className="w-full py-4 bg-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] text-white font-bold rounded-xl shadow-lg flex items-center justify-center gap-2"
|
|
>
|
|
Confirm Transition
|
|
<ArrowRight size={20} />
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|