2025-12-01 14:36:25 +00:00
|
|
|
import type { ColourData, PaintedCell, Region } from "../../../../types/types";
|
2025-11-27 16:16:15 +00:00
|
|
|
import ColourPicker from "./ColourPicker";
|
|
|
|
|
import { useCameraFeedContext } from "../../../../app/context/CameraFeedContext";
|
2025-12-01 14:36:25 +00:00
|
|
|
import { useColourDectection } from "../../hooks/useColourDetection";
|
2025-11-21 16:01:34 +00:00
|
|
|
|
|
|
|
|
type RegionSelectorProps = {
|
|
|
|
|
regions: Region[];
|
|
|
|
|
selectedRegionIndex: number;
|
2025-11-24 13:29:00 +00:00
|
|
|
mode: string;
|
2025-11-27 16:16:15 +00:00
|
|
|
cameraFeedID: "A" | "B" | "C";
|
2025-11-21 16:01:34 +00:00
|
|
|
};
|
|
|
|
|
|
2025-11-27 16:16:15 +00:00
|
|
|
const RegionSelector = ({ regions, selectedRegionIndex, mode, cameraFeedID }: RegionSelectorProps) => {
|
2025-12-01 14:36:25 +00:00
|
|
|
const { colourMutation } = useColourDectection();
|
|
|
|
|
const { state, dispatch } = useCameraFeedContext();
|
|
|
|
|
const paintedCells = state.paintedCells[cameraFeedID];
|
|
|
|
|
|
2025-11-24 13:29:00 +00:00
|
|
|
const handleChange = (e: { target: { value: string } }) => {
|
2025-11-27 16:16:15 +00:00
|
|
|
dispatch({ type: "CHANGE_MODE", payload: { cameraFeedID: cameraFeedID, mode: e.target.value } });
|
2025-11-23 22:36:08 +00:00
|
|
|
};
|
|
|
|
|
|
2025-11-27 16:16:15 +00:00
|
|
|
const handleResetRegion = () => {
|
|
|
|
|
dispatch({
|
|
|
|
|
type: "RESET_PAINTED_CELLS",
|
|
|
|
|
payload: { cameraFeedID: cameraFeedID, paintedCells: new Map<string, PaintedCell>() },
|
|
|
|
|
});
|
2025-11-25 14:57:18 +00:00
|
|
|
};
|
|
|
|
|
|
2025-11-27 16:16:15 +00:00
|
|
|
const handleModeChange = (newMode: string) => {
|
|
|
|
|
dispatch({ type: "CHANGE_MODE", payload: { cameraFeedID: cameraFeedID, mode: newMode } });
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleRegionSelect = (index: number) => {
|
|
|
|
|
dispatch({ type: "SET_SELECTED_REGION_INDEX", payload: index });
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleRegionColourChange = (index: number, newColour: string) => {
|
|
|
|
|
const regionName = regions[index].name;
|
|
|
|
|
|
|
|
|
|
dispatch({
|
|
|
|
|
type: "SET_SELECTED_REGION_COLOUR",
|
|
|
|
|
payload: { cameraFeedID: cameraFeedID, regionName: regionName, newColour: newColour },
|
|
|
|
|
});
|
2025-11-25 20:49:11 +00:00
|
|
|
};
|
|
|
|
|
|
2025-12-04 19:14:14 +00:00
|
|
|
const handleAddRegionClick = () => {
|
|
|
|
|
const regionName = `Bay ${regions.length + 1}`;
|
|
|
|
|
dispatch({
|
|
|
|
|
type: "ADD_NEW_REGION",
|
|
|
|
|
payload: { cameraFeedID: cameraFeedID, regionName: regionName, brushColour: "#ffffff" },
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleRemoveClick = () => {
|
|
|
|
|
dispatch({
|
|
|
|
|
type: "REMOVE_REGION",
|
|
|
|
|
payload: { cameraFeedID: cameraFeedID, regionName: regions[selectedRegionIndex].name },
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2025-12-01 14:36:25 +00:00
|
|
|
const handleSaveclick = () => {
|
|
|
|
|
const regions: ColourData[] = [];
|
|
|
|
|
const test = Array.from(paintedCells.entries());
|
2025-12-04 19:14:14 +00:00
|
|
|
const region1 = test.filter(([, cell]) => cell.region.name === "Bay 1");
|
|
|
|
|
const region2 = test.filter(([, cell]) => cell.region.name === "Bay 2");
|
|
|
|
|
const region3 = test.filter(([, cell]) => cell.region.name === "Bay 3");
|
|
|
|
|
const region4 = test.filter(([, cell]) => cell.region.name === "Bay 4");
|
|
|
|
|
const region5 = test.filter(([, cell]) => cell.region.name === "Bay 5");
|
2025-12-01 14:36:25 +00:00
|
|
|
const region1Data = {
|
|
|
|
|
id: 1,
|
|
|
|
|
cells: region1.map(([key]) => [parseInt(key.split("-")[1]), parseInt(key.split("-")[0])]),
|
|
|
|
|
};
|
|
|
|
|
const region2Data = {
|
|
|
|
|
id: 2,
|
|
|
|
|
cells: region2.map(([key]) => [parseInt(key.split("-")[1]), parseInt(key.split("-")[0])]),
|
|
|
|
|
};
|
|
|
|
|
const region3Data = {
|
|
|
|
|
id: 3,
|
|
|
|
|
cells: region3.map(([key]) => [parseInt(key.split("-")[1]), parseInt(key.split("-")[0])]),
|
|
|
|
|
};
|
|
|
|
|
const region4Data = {
|
|
|
|
|
id: 4,
|
|
|
|
|
cells: region4.map(([key]) => [parseInt(key.split("-")[1]), parseInt(key.split("-")[0])]),
|
|
|
|
|
};
|
|
|
|
|
const region5Data = {
|
|
|
|
|
id: 5,
|
|
|
|
|
cells: region5.map(([key]) => [parseInt(key.split("-")[1]), parseInt(key.split("-")[0])]),
|
|
|
|
|
};
|
|
|
|
|
if (region1Data.cells.length > 0) {
|
|
|
|
|
regions.push(region1Data);
|
|
|
|
|
}
|
|
|
|
|
if (region2Data.cells.length > 0) {
|
|
|
|
|
regions.push(region2Data);
|
|
|
|
|
}
|
|
|
|
|
if (region3Data.cells.length > 0) {
|
|
|
|
|
regions.push(region3Data);
|
|
|
|
|
}
|
|
|
|
|
if (region4Data.cells.length > 0) {
|
|
|
|
|
regions.push(region4Data);
|
|
|
|
|
}
|
|
|
|
|
if (region5Data.cells.length > 0) {
|
|
|
|
|
regions.push(region5Data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
colourMutation.mutate({ cameraFeedID, regions: regions });
|
|
|
|
|
};
|
|
|
|
|
|
2025-11-21 16:01:34 +00:00
|
|
|
return (
|
2025-11-25 14:57:18 +00:00
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 md:grid-rows-2 gap-4">
|
|
|
|
|
<div className="p-2 border border-gray-600 rounded-lg flex flex-col">
|
|
|
|
|
<h2 className="text-2xl mb-2">Tools</h2>
|
2025-11-24 13:29:00 +00:00
|
|
|
<div className="flex flex-col">
|
2025-11-25 14:57:18 +00:00
|
|
|
<label
|
|
|
|
|
htmlFor="paintMode"
|
|
|
|
|
className={`p-4 border rounded-lg mb-2
|
|
|
|
|
${mode === "painter" ? "border-gray-400 bg-[#202b36]" : "bg-[#253445] border-gray-700"}
|
|
|
|
|
hover:bg-[#202b36] hover:cursor-pointer`}
|
|
|
|
|
>
|
|
|
|
|
<input
|
|
|
|
|
id="paintMode"
|
|
|
|
|
type="radio"
|
|
|
|
|
onChange={handleChange}
|
|
|
|
|
checked={mode === "painter"}
|
|
|
|
|
value="painter"
|
|
|
|
|
className="sr-only"
|
|
|
|
|
/>
|
|
|
|
|
<span className="text-xl">Paint mode</span>
|
2025-11-24 13:29:00 +00:00
|
|
|
</label>
|
2025-11-25 14:57:18 +00:00
|
|
|
<label
|
|
|
|
|
htmlFor="eraseMode"
|
|
|
|
|
className={`p-4 border rounded-lg mb-2
|
|
|
|
|
${mode === "eraser" ? "border-gray-400 bg-[#202b36]" : "bg-[#253445] border-gray-700"}
|
|
|
|
|
hover:bg-[#202b36] hover:cursor-pointer`}
|
|
|
|
|
>
|
|
|
|
|
<input
|
|
|
|
|
id="eraseMode"
|
|
|
|
|
type="radio"
|
|
|
|
|
onChange={handleChange}
|
|
|
|
|
checked={mode === "eraser"}
|
|
|
|
|
value={"eraser"}
|
|
|
|
|
className="sr-only"
|
|
|
|
|
/>
|
|
|
|
|
<span className="text-xl">Erase mode</span>
|
2025-11-24 13:29:00 +00:00
|
|
|
</label>
|
|
|
|
|
</div>
|
2025-11-21 16:01:34 +00:00
|
|
|
</div>
|
2025-11-25 14:57:18 +00:00
|
|
|
<div className="p-2 border border-gray-600 rounded-lg flex flex-col">
|
2025-12-04 19:14:14 +00:00
|
|
|
<h2 className="text-2xl mb-2">Bay Select</h2>
|
2025-11-25 14:57:18 +00:00
|
|
|
<>
|
2025-11-27 16:16:15 +00:00
|
|
|
{regions?.map((region, idx) => {
|
2025-11-25 14:57:18 +00:00
|
|
|
const isSelected = selectedRegionIndex === idx;
|
|
|
|
|
const inputId = `region-${idx}`;
|
|
|
|
|
return (
|
|
|
|
|
<label
|
|
|
|
|
htmlFor={inputId}
|
|
|
|
|
key={region.name}
|
2025-12-01 14:36:25 +00:00
|
|
|
className={`items-center p-4 m-1 rounded-xl border flex flex-row justify-between
|
2025-11-25 14:57:18 +00:00
|
|
|
${isSelected ? "border-gray-400 bg-[#202b36]" : "bg-[#253445] border-gray-700"} hover:bg-[#202b36] hover:cursor-pointer`}
|
|
|
|
|
>
|
|
|
|
|
<div className="flex flex-row gap-4 items-center">
|
|
|
|
|
<input
|
|
|
|
|
type="radio"
|
|
|
|
|
checked={isSelected}
|
|
|
|
|
id={inputId}
|
|
|
|
|
name="region"
|
|
|
|
|
className="sr-only"
|
|
|
|
|
onChange={() => {
|
2025-11-27 16:16:15 +00:00
|
|
|
handleModeChange("painter");
|
|
|
|
|
handleRegionSelect(idx);
|
2025-11-25 14:57:18 +00:00
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
<span className="text-xl">{region.name}</span>
|
|
|
|
|
</div>
|
2025-11-27 16:16:15 +00:00
|
|
|
<ColourPicker colour={region.brushColour} setColour={(c: string) => handleRegionColourChange(idx, c)} />
|
2025-12-01 14:36:25 +00:00
|
|
|
<div></div>
|
2025-11-25 14:57:18 +00:00
|
|
|
</label>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</>
|
2025-12-04 19:14:14 +00:00
|
|
|
<div className="flex flex-col gap-4 mt-4">
|
|
|
|
|
<button className="border border-blue-900 bg-blue-700 px-4 py-1 rounded-md" onClick={handleAddRegionClick}>
|
|
|
|
|
Add Bay
|
|
|
|
|
</button>
|
|
|
|
|
<button className="border border-red-900 bg-red-700 px-4 py-1 rounded-md" onClick={handleRemoveClick}>
|
|
|
|
|
Remove Bay
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
2025-11-25 14:57:18 +00:00
|
|
|
</div>
|
|
|
|
|
|
2025-11-25 15:49:53 +00:00
|
|
|
<div className="p-2 border border-gray-600 rounded-lg flex flex-col md:col-span-2 h-50">
|
2025-11-25 14:57:18 +00:00
|
|
|
<div className="flex flex-col">
|
|
|
|
|
<h2 className="text-2xl mb-2">Actions</h2>
|
2025-12-01 14:36:25 +00:00
|
|
|
<button
|
|
|
|
|
onClick={handleSaveclick}
|
|
|
|
|
className="mt-2 px-4 py-2 border border-blue-600 rounded-md text-white bg-blue-600 w-full md:w-[40%] hover:bg-blue-700 hover:cursor-pointer"
|
|
|
|
|
>
|
|
|
|
|
Save Region
|
|
|
|
|
</button>
|
2025-11-25 14:57:18 +00:00
|
|
|
<button
|
2025-11-27 16:16:15 +00:00
|
|
|
onClick={handleResetRegion}
|
2025-11-25 14:57:18 +00:00
|
|
|
className="mt-2 px-4 py-2 border border-red-600 rounded-md text-white bg-red-600 w-full md:w-[40%] hover:bg-red-700 hover:cursor-pointer"
|
|
|
|
|
>
|
2025-11-27 16:16:15 +00:00
|
|
|
Reset Region
|
2025-11-25 14:57:18 +00:00
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-11-21 16:01:34 +00:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default RegionSelector;
|