Login sequence and inventory/protocol storage groundwork
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import { useState, useRef } from "react";
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import { chemicalsApi } from "../lib/api";
|
||||
import type { ChemicalInventory } from "../shared/types";
|
||||
import { Card } from "./ui/card";
|
||||
import { Button } from "./ui/button";
|
||||
import { Input } from "./ui/input";
|
||||
@@ -34,47 +36,6 @@ import {
|
||||
SelectValue,
|
||||
} from "./ui/select";
|
||||
|
||||
interface ChemicalInventory {
|
||||
id: string;
|
||||
// Required fields (red)
|
||||
piFirstName: string;
|
||||
physicalState: string;
|
||||
chemicalName: string;
|
||||
bldgCode: string;
|
||||
lab: string;
|
||||
storageLocation: string;
|
||||
storageDevice: string;
|
||||
numberOfContainers: string;
|
||||
amountPerContainer: string;
|
||||
unitOfMeasure: string;
|
||||
casNumber: string;
|
||||
// Nice to have fields
|
||||
chemicalFormula?: string;
|
||||
molecularWeight?: string;
|
||||
vendor?: string;
|
||||
catalogNumber?: string;
|
||||
foundInCatalog?: string;
|
||||
poNumber?: string;
|
||||
receiptDate?: string;
|
||||
openDate?: string;
|
||||
maxOnHand?: string;
|
||||
expirationDate?: string;
|
||||
contact?: string;
|
||||
comments?: string;
|
||||
dateEntered?: string;
|
||||
permitNumber?: string;
|
||||
barcode?: string;
|
||||
lastChanged?: string;
|
||||
concentration?: string;
|
||||
chemicalNumber?: string;
|
||||
lotNumber?: string;
|
||||
multipleCAS?: string;
|
||||
msds?: string;
|
||||
// Special fields
|
||||
percentageFull?: number; // blue
|
||||
needsManualEntry?: string[]; // yellow highlight
|
||||
scannedImage?: string;
|
||||
}
|
||||
|
||||
const storageDeviceOptions = [
|
||||
"Aerosol Can",
|
||||
@@ -102,71 +63,15 @@ export function Inventory() {
|
||||
const [shareDialogOpen, setShareDialogOpen] = useState(false);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const [inventory, setInventory] = useState<ChemicalInventory[]>([
|
||||
{
|
||||
id: "1",
|
||||
piFirstName: "Dr. Smith",
|
||||
physicalState: "Liquid",
|
||||
chemicalName: "Acetone",
|
||||
bldgCode: "BLD-A",
|
||||
lab: "Lab 201",
|
||||
storageLocation: "Cabinet A-3",
|
||||
storageDevice: "Glass Bottle",
|
||||
numberOfContainers: "2",
|
||||
amountPerContainer: "1",
|
||||
unitOfMeasure: "L",
|
||||
casNumber: "67-64-1",
|
||||
chemicalFormula: "C3H6O",
|
||||
molecularWeight: "58.08",
|
||||
vendor: "Sigma-Aldrich",
|
||||
catalogNumber: "179124",
|
||||
expirationDate: "2025-06-15",
|
||||
lotNumber: "SLCD1234",
|
||||
percentageFull: 65,
|
||||
dateEntered: "2024-11-15",
|
||||
openDate: "2024-11-15"
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
piFirstName: "Dr. Johnson",
|
||||
physicalState: "Solid",
|
||||
chemicalName: "Sodium Hydroxide",
|
||||
bldgCode: "BLD-B",
|
||||
lab: "Lab 305",
|
||||
storageLocation: "Cabinet B-1",
|
||||
storageDevice: "Plastic Bottle",
|
||||
numberOfContainers: "1",
|
||||
amountPerContainer: "500",
|
||||
unitOfMeasure: "g",
|
||||
casNumber: "1310-73-2",
|
||||
chemicalFormula: "NaOH",
|
||||
molecularWeight: "39.997",
|
||||
vendor: "Fisher Scientific",
|
||||
expirationDate: "2025-10-22",
|
||||
lotNumber: "FS9876",
|
||||
percentageFull: 15,
|
||||
dateEntered: "2024-10-22"
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
piFirstName: "Dr. Smith",
|
||||
physicalState: "Liquid",
|
||||
chemicalName: "Hydrochloric Acid",
|
||||
bldgCode: "BLD-A",
|
||||
lab: "Lab 201",
|
||||
storageLocation: "Acid Cabinet",
|
||||
storageDevice: "Glass Bottle",
|
||||
numberOfContainers: "1",
|
||||
amountPerContainer: "1",
|
||||
unitOfMeasure: "L",
|
||||
casNumber: "7647-01-0",
|
||||
chemicalFormula: "HCl",
|
||||
vendor: "Fisher Scientific",
|
||||
expirationDate: "2024-12-10",
|
||||
percentageFull: 80,
|
||||
dateEntered: "2024-08-15"
|
||||
}
|
||||
]);
|
||||
const [inventory, setInventory] = useState<ChemicalInventory[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
chemicalsApi.list()
|
||||
.then(data => setInventory(data))
|
||||
.catch(console.error)
|
||||
.finally(() => setIsLoading(false));
|
||||
}, []);
|
||||
|
||||
const handlePhotoCapture = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = event.target.files?.[0];
|
||||
@@ -211,38 +116,30 @@ export function Inventory() {
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
const handleAddFromScan = () => {
|
||||
const handleAddFromScan = async () => {
|
||||
if (extractedData) {
|
||||
const newChemical: ChemicalInventory = {
|
||||
id: Date.now().toString(),
|
||||
piFirstName: extractedData.piFirstName || "",
|
||||
physicalState: extractedData.physicalState || "",
|
||||
chemicalName: extractedData.chemicalName || "",
|
||||
bldgCode: extractedData.bldgCode || "",
|
||||
lab: extractedData.lab || "",
|
||||
storageLocation: extractedData.storageLocation || "",
|
||||
storageDevice: extractedData.storageDevice || "Glass Bottle",
|
||||
numberOfContainers: extractedData.numberOfContainers || "1",
|
||||
amountPerContainer: extractedData.amountPerContainer || "",
|
||||
unitOfMeasure: extractedData.unitOfMeasure || "",
|
||||
casNumber: extractedData.casNumber || "",
|
||||
chemicalFormula: extractedData.chemicalFormula,
|
||||
molecularWeight: extractedData.molecularWeight,
|
||||
vendor: extractedData.vendor,
|
||||
catalogNumber: extractedData.catalogNumber,
|
||||
expirationDate: extractedData.expirationDate,
|
||||
lotNumber: extractedData.lotNumber,
|
||||
concentration: extractedData.concentration,
|
||||
percentageFull: extractedData.percentageFull,
|
||||
dateEntered: new Date().toISOString().split('T')[0],
|
||||
needsManualEntry: extractedData.needsManualEntry,
|
||||
scannedImage: extractedData.scannedImage
|
||||
};
|
||||
|
||||
setInventory([newChemical, ...inventory]);
|
||||
setCapturedImage(null);
|
||||
setExtractedData(null);
|
||||
setIsPhotoDialogOpen(false);
|
||||
try {
|
||||
const saved = await chemicalsApi.create({
|
||||
...extractedData,
|
||||
piFirstName: extractedData.piFirstName || "",
|
||||
physicalState: extractedData.physicalState || "",
|
||||
chemicalName: extractedData.chemicalName || "",
|
||||
bldgCode: extractedData.bldgCode || "",
|
||||
lab: extractedData.lab || "",
|
||||
storageLocation: extractedData.storageLocation || "",
|
||||
storageDevice: extractedData.storageDevice || "Glass Bottle",
|
||||
numberOfContainers: extractedData.numberOfContainers || "1",
|
||||
amountPerContainer: extractedData.amountPerContainer || "",
|
||||
unitOfMeasure: extractedData.unitOfMeasure || "",
|
||||
casNumber: extractedData.casNumber || "",
|
||||
});
|
||||
setInventory([saved, ...inventory]);
|
||||
setCapturedImage(null);
|
||||
setExtractedData(null);
|
||||
setIsPhotoDialogOpen(false);
|
||||
} catch (err) {
|
||||
console.error("Failed to save chemical:", err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -615,6 +512,9 @@ export function Inventory() {
|
||||
</div>
|
||||
|
||||
{/* Spreadsheet Table */}
|
||||
{isLoading && (
|
||||
<p className="text-center text-muted-foreground py-8">Loading inventory...</p>
|
||||
)}
|
||||
<Card className="overflow-hidden">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
|
||||
Reference in New Issue
Block a user