```react import React, { useState, useEffect, useMemo } from 'react'; import { initializeApp } from 'firebase/app'; import { getAuth, signInAnonymously, onAuthStateChanged, signInWithCustomToken } from 'firebase/auth'; import { getFirestore, collection, onSnapshot, addDoc, serverTimestamp } from 'firebase/firestore'; import { Settings, BarChart3, Clock, Calendar, Download, LogOut, PlusCircle, Factory, User, CheckCircle2 } from 'lucide-react'; // --- Firebase Initialization --- const firebaseConfig = typeof __firebase_config !== 'undefined' ? JSON.parse(__firebase_config) : {}; const app = initializeApp(firebaseConfig); const auth = getAuth(app); const db = getFirestore(app); const appId = typeof __app_id !== 'undefined' ? __app_id : 'loom-system-app'; // --- Helper Functions for Time & Shifts --- const getProductionInfo = (inputDate = new Date()) => { const d = new Date(inputDate); const hours = d.getHours(); // If time is between midnight and 7 AM, it belongs to yesterday's production day if (hours < 7) { d.setDate(d.getDate() - 1); } const year = d.getFullYear(); const month = String(d.getMonth() + 1).padStart(2, '0'); const day = String(d.getDate()).padStart(2, '0'); const shift = (hours >= 7 && hours < 19) ? 'Day' : 'Night'; return { dateString: `${year}-${month}-${day}`, shift: shift }; }; const DAY_SLOTS = ["07:00 - 09:00", "09:00 - 11:00", "11:00 - 13:00", "13:00 - 15:00", "15:00 - 17:00", "17:00 - 19:00"]; const NIGHT_SLOTS = ["19:00 - 21:00", "21:00 - 23:00", "23:00 - 01:00", "01:00 - 03:00", "03:00 - 05:00", "05:00 - 07:00"]; export default function App() { const [fbUser, setFbUser] = useState(null); const [userRole, setUserRole] = useState(null); // 'admin' or 'user' const [records, setRecords] = useState([]); const [loading, setLoading] = useState(true); // --- Firebase Auth & Data Fetching --- useEffect(() => { const initAuth = async () => { try { if (typeof __initial_auth_token !== 'undefined' && __initial_auth_token) { await signInWithCustomToken(auth, __initial_auth_token); } else { await signInAnonymously(auth); } } catch (err) { console.error("Auth error:", err); } }; initAuth(); const unsubscribeAuth = onAuthStateChanged(auth, (user) => { setFbUser(user); }); return () => unsubscribeAuth(); }, []); useEffect(() => { if (!fbUser) return; const recordsRef = collection(db, 'artifacts', appId, 'public', 'data', 'production_logs'); const unsubscribeData = onSnapshot(recordsRef, (snapshot) => { const data = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })); // Sort by timestamp descending on client side data.sort((a, b) => (b.timestamp?.toMillis() || 0) - (a.timestamp?.toMillis() || 0)); setRecords(data); setLoading(false); }, (error) => { console.error("Firestore error:", error); setLoading(false); } ); return () => unsubscribeData(); }, [fbUser]); // --- Login Screen --- if (!userRole) { return (

Production Management

Select your portal to continue

); } return (
{/* Navbar */} {/* Main Content */}
{loading ? (
) : ( userRole === 'admin' ? : )}
); } // ========================================== // USER DASHBOARD (DATA ENTRY) // ========================================== function UserDashboard({ fbUser }) { const currentInfo = getProductionInfo(new Date()); const [formData, setFormData] = useState({ loomId: '1', prodDate: currentInfo.dateString, shift: currentInfo.shift, timeSlot: currentInfo.shift === 'Day' ? DAY_SLOTS[0] : NIGHT_SLOTS[0], meters: '' }); const [message, setMessage] = useState({ text: '', type: '' }); const availableSlots = formData.shift === 'Day' ? DAY_SLOTS : NIGHT_SLOTS; useEffect(() => { setFormData(prev => ({ ...prev, timeSlot: prev.shift === 'Day' ? DAY_SLOTS[0] : NIGHT_SLOTS[0] })); }, [formData.shift]); const handleSubmit = async (e) => { e.preventDefault(); if (!formData.meters || formData.meters <= 0) { setMessage({ text: 'Please enter a valid meter quantity.', type: 'error' }); return; } try { const recordsRef = collection(db, 'artifacts', appId, 'public', 'data', 'production_logs'); await addDoc(recordsRef, { ...formData, meters: Number(formData.meters), timestamp: serverTimestamp(), userId: fbUser.uid }); setMessage({ text: 'Record saved successfully!', type: 'success' }); setFormData(prev => ({ ...prev, meters: '' })); setTimeout(() => setMessage({ text: '', type: '' }), 3000); } catch (error) { console.error("Error adding document: ", error); setMessage({ text: 'Error saving record. Please try again.', type: 'error' }); } }; return (

Record Loom Production

Production Day: {currentInfo.dateString}

{message.text && (
{message.text}
)}
setFormData({...formData, prodDate: e.target.value})} className="w-full p-3 border border-slate-300 rounded-xl focus:ring-2 focus:ring-indigo-500 outline-none" />
setFormData({...formData, meters: e.target.value})} className="w-full p-4 text-xl border-2 border-indigo-200 rounded-xl focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none" />
); } // ========================================== // ADMIN DASHBOARD // ========================================== function AdminDashboard({ records }) { const currentInfo = getProductionInfo(new Date()); const [filters, setFilters] = useState({ startDate: currentInfo.dateString, endDate: currentInfo.dateString, loomId: 'all', shift: 'all' }); const filteredRecords = useMemo(() => { return records.filter(record => { const matchStart = record.prodDate >= filters.startDate; const matchEnd = record.prodDate <= filters.endDate; const matchLoom = filters.loomId === 'all' || record.loomId === filters.loomId; const matchShift = filters.shift === 'all' || record.shift === filters.shift; return matchStart && matchEnd && matchLoom && matchShift; }); }, [records, filters]); const totalMeters = filteredRecords.reduce((sum, r) => sum + (Number(r.meters) || 0), 0); const activeLooms = new Set(filteredRecords.map(r => r.loomId)).size; const totalEntries = filteredRecords.length; const exportToCSV = () => { if (filteredRecords.length === 0) return; const headers = ['Date', 'Shift', 'Time Slot', 'Loom Number', 'Meters Produced']; const csvContent = [ headers.join(','), ...filteredRecords.map(r => `${r.prodDate},${r.shift},${r.timeSlot},Loom ${r.loomId},${r.meters}`) ].join('\n'); const blob = new Blob(['\uFEFF' + csvContent], { type: 'text/csv;charset=utf-8;' }); const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.setAttribute("href", url); link.setAttribute("download", `data_${filters.startDate}_to_${filters.endDate}.csv`); document.body.appendChild(link); link.click(); document.body.removeChild(link); }; return (
{/* Filters Card */}

Filters & Search

setFilters({...filters, startDate: e.target.value})} className="w-full p-2 border border-slate-300 rounded-lg outline-none focus:border-indigo-500" />
setFilters({...filters, endDate: e.target.value})} className="w-full p-2 border border-slate-300 rounded-lg outline-none focus:border-indigo-500" />
{/* Stats Cards */}

Total Meters Produced

{totalMeters.toLocaleString()} m

Active Looms

{activeLooms} / 150

Total Records

{totalEntries} entries

{/* Data Table */}

Production Data Log

{filteredRecords.length > 0 ? ( filteredRecords.map((record) => ( )) ) : ( )}
Date Shift Time Slot Loom Meters
{record.prodDate} {record.shift} {record.timeSlot} Loom {record.loomId} {record.meters} m
No production records found for the selected filters.
); } ```