204 lines
6.8 KiB
TypeScript
204 lines
6.8 KiB
TypeScript
import { useState } from "react";
|
|
import { Card } from "./ui/card";
|
|
import { Button } from "./ui/button";
|
|
import { Input } from "./ui/input";
|
|
import { Badge } from "./ui/badge";
|
|
import {
|
|
Search,
|
|
Download,
|
|
ExternalLink,
|
|
AlertTriangle,
|
|
Flame,
|
|
Skull,
|
|
Droplet,
|
|
FileText
|
|
} from "lucide-react";
|
|
|
|
interface SDS {
|
|
id: string;
|
|
chemicalName: string;
|
|
casNumber: string;
|
|
manufacturer: string;
|
|
revisionDate: string;
|
|
hazards: string[];
|
|
pictograms: string[];
|
|
}
|
|
|
|
export function SDSLibrary() {
|
|
const [searchQuery, setSearchQuery] = useState("");
|
|
|
|
const sdsDatabase: SDS[] = [
|
|
{
|
|
id: "1",
|
|
chemicalName: "Acetone",
|
|
casNumber: "67-64-1",
|
|
manufacturer: "Sigma-Aldrich",
|
|
revisionDate: "2024-08-15",
|
|
hazards: ["Flammable", "Eye Irritation"],
|
|
pictograms: ["flame", "exclamation"]
|
|
},
|
|
{
|
|
id: "2",
|
|
chemicalName: "Sodium Hydroxide",
|
|
casNumber: "1310-73-2",
|
|
manufacturer: "Fisher Scientific",
|
|
revisionDate: "2024-09-20",
|
|
hazards: ["Corrosive", "Skin Burns", "Eye Damage"],
|
|
pictograms: ["corrosion"]
|
|
},
|
|
{
|
|
id: "3",
|
|
chemicalName: "Benzene",
|
|
casNumber: "71-43-2",
|
|
manufacturer: "VWR",
|
|
revisionDate: "2024-07-10",
|
|
hazards: ["Carcinogenic", "Flammable", "Toxic"],
|
|
pictograms: ["flame", "health-hazard", "exclamation"]
|
|
},
|
|
{
|
|
id: "4",
|
|
chemicalName: "Hydrochloric Acid",
|
|
casNumber: "7647-01-0",
|
|
manufacturer: "Sigma-Aldrich",
|
|
revisionDate: "2024-10-05",
|
|
hazards: ["Corrosive", "Acute Toxicity"],
|
|
pictograms: ["corrosion", "exclamation"]
|
|
},
|
|
{
|
|
id: "5",
|
|
chemicalName: "Ethanol",
|
|
casNumber: "64-17-5",
|
|
manufacturer: "Fisher Scientific",
|
|
revisionDate: "2024-11-01",
|
|
hazards: ["Flammable"],
|
|
pictograms: ["flame"]
|
|
}
|
|
];
|
|
|
|
const filteredSDS = sdsDatabase.filter(sds =>
|
|
sds.chemicalName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
|
sds.casNumber.includes(searchQuery)
|
|
);
|
|
|
|
const getPictogramIcon = (pictogram: string) => {
|
|
switch (pictogram) {
|
|
case "flame": return <Flame className="w-5 h-5" />;
|
|
case "corrosion": return <Droplet className="w-5 h-5" />;
|
|
case "health-hazard": return <Skull className="w-5 h-5" />;
|
|
case "exclamation": return <AlertTriangle className="w-5 h-5" />;
|
|
default: return <AlertTriangle className="w-5 h-5" />;
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="p-8">
|
|
<div className="max-w-6xl mx-auto">
|
|
<div className="mb-8">
|
|
<h1 className="text-foreground mb-2">Safety Data Sheets Library</h1>
|
|
<p className="text-muted-foreground">Access comprehensive safety information for chemicals in your lab</p>
|
|
</div>
|
|
|
|
{/* Search */}
|
|
<Card className="p-4 mb-6">
|
|
<div className="relative">
|
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground w-5 h-5" />
|
|
<Input
|
|
placeholder="Search by chemical name or CAS number..."
|
|
className="pl-10"
|
|
value={searchQuery}
|
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
/>
|
|
</div>
|
|
</Card>
|
|
|
|
{/* Quick Stats */}
|
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
|
|
<Card className="p-4">
|
|
<p className="text-muted-foreground mb-1">Total SDS</p>
|
|
<p className="text-foreground">{sdsDatabase.length}</p>
|
|
</Card>
|
|
<Card className="p-4">
|
|
<p className="text-muted-foreground mb-1">Updated This Month</p>
|
|
<p className="text-foreground">2</p>
|
|
</Card>
|
|
<Card className="p-4">
|
|
<p className="text-muted-foreground mb-1">Expiring Soon</p>
|
|
<p className="text-amber-600">1</p>
|
|
</Card>
|
|
<Card className="p-4">
|
|
<p className="text-muted-foreground mb-1">Downloads</p>
|
|
<p className="text-foreground">156</p>
|
|
</Card>
|
|
</div>
|
|
|
|
{/* SDS List */}
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
{filteredSDS.map((sds) => (
|
|
<Card key={sds.id} className="p-6 hover:shadow-md transition-shadow">
|
|
<div className="flex items-start justify-between mb-4">
|
|
<div className="flex-1">
|
|
<h3 className="text-foreground mb-1">{sds.chemicalName}</h3>
|
|
<p className="text-muted-foreground">CAS: {sds.casNumber}</p>
|
|
</div>
|
|
<FileText className="w-6 h-6 text-muted-foreground" />
|
|
</div>
|
|
|
|
<div className="space-y-3 mb-4">
|
|
<div>
|
|
<p className="text-muted-foreground mb-1">Manufacturer</p>
|
|
<p className="text-foreground">{sds.manufacturer}</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-muted-foreground mb-1">Revision Date</p>
|
|
<p className="text-foreground">{sds.revisionDate}</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-muted-foreground mb-2">Hazard Pictograms</p>
|
|
<div className="flex gap-2">
|
|
{sds.pictograms.map((pictogram, idx) => (
|
|
<div
|
|
key={idx}
|
|
className="w-10 h-10 border-2 border-red-500 rounded flex items-center justify-center text-red-600"
|
|
>
|
|
{getPictogramIcon(pictogram)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<p className="text-muted-foreground mb-2">Key Hazards</p>
|
|
<div className="flex flex-wrap gap-2">
|
|
{sds.hazards.map((hazard, idx) => (
|
|
<Badge key={idx} variant="outline" className="text-orange-700 border-orange-300">
|
|
{hazard}
|
|
</Badge>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex gap-2 pt-4 border-t border-border">
|
|
<Button variant="outline" className="flex-1">
|
|
<Download className="w-4 h-4 mr-2" />
|
|
Download
|
|
</Button>
|
|
<Button variant="outline" className="flex-1">
|
|
<ExternalLink className="w-4 h-4 mr-2" />
|
|
View
|
|
</Button>
|
|
</div>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
|
|
{filteredSDS.length === 0 && (
|
|
<Card className="p-12 text-center">
|
|
<FileText className="w-12 h-12 text-gray-400 mx-auto mb-4" />
|
|
<h3 className="text-gray-900 mb-2">No SDS Found</h3>
|
|
<p className="text-gray-600">Try searching with a different chemical name or CAS number</p>
|
|
</Card>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
} |