ca-grow-ops-manager/frontend/src/components/BatchTransitionModal.tsx
fullsizemalt 47de301f77
Some checks are pending
Test / backend-test (push) Waiting to run
Test / frontend-test (push) Waiting to run
style: Dark/Light mode contrast audit
- 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
2025-12-27 12:12:10 -08:00

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>
);
}