diff --git a/src/components/Header.tsx b/src/components/Header.tsx index b655716..18f8768 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -8,6 +8,7 @@ import LanguageSwitcher from './LanguageSwitcher'; import Sidebar from './Sidebar'; const Header: React.FC = () => { + const [isSidebarOpen, setIsSidebarOpen] = React.useState(false); const location = useLocation(); const [isSidebarOpen, setIsSidebarOpen] = useState(false); const { theme, toggleTheme } = useTheme(); diff --git a/src/components/Timeline.tsx b/src/components/Timeline.tsx deleted file mode 100644 index 8e760e7..0000000 --- a/src/components/Timeline.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import React from "react"; -import { motion } from "framer-motion"; -import { - Sprout, - Truck, - Store, - MapPin, - User, - Clock, -} from "lucide-react"; - -type Stage = "farmer" | "transport" | "retailer"; -type Status = "completed" | "pending" | "flagged"; - -interface TimelineEvent { - stage: Stage; - actor: string; - location: string; - timestamp: string; - status: Status; - notes?: string; -} - -interface TimelineProps { - events: TimelineEvent[]; -} - -const stageIconMap = { - farmer: Sprout, - transport: Truck, - retailer: Store, -}; - -const statusStyles = { - completed: { - dot: "bg-green-500", - badge: "text-green-600 bg-green-100", - }, - pending: { - dot: "bg-gray-400", - badge: "text-gray-600 bg-gray-100", - }, - flagged: { - dot: "bg-red-500", - badge: "text-red-600 bg-red-100", - }, -}; - -const Timeline: React.FC = ({ events }) => { - const formatDate = (dateString: string) => - new Date(dateString).toLocaleString("en-US", { - dateStyle: "medium", - timeStyle: "short", - }); - - return ( -
- {/* Vertical line */} -
- -
- {events.map((event, index) => { - const Icon = stageIconMap[event.stage]; - const styles = statusStyles[event.status]; - - return ( - - {/* Timeline dot */} -
- -
- - {/* Card */} -
-
-

- {event.stage} -

- - {event.status} - -
- -
- - {formatDate(event.timestamp)} -
- -
-
- - {event.actor} -
-
- - {event.location} -
-
- - {event.notes && ( -
- {event.notes} -
- )} -
-
- ); - })} -
-
- ); -}; - -export default Timeline; diff --git a/src/components/Timeline/Timeline.tsx b/src/components/Timeline/Timeline.tsx new file mode 100644 index 0000000..3fb3897 --- /dev/null +++ b/src/components/Timeline/Timeline.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import TimelineStep from './TimelineStep'; +import { TimelineEvent } from './types'; + +interface TimelineProps { + events: TimelineEvent[]; + globalCertifications?: string; +} + +const Timeline: React.FC = ({ events, globalCertifications }) => { + return ( +
+ {/* Vertical line */} +
+ +
+ {events.map((event, index) => ( + + ))} +
+
+ ); +}; + +export default Timeline; diff --git a/src/components/Timeline/TimelineStep.tsx b/src/components/Timeline/TimelineStep.tsx new file mode 100644 index 0000000..9161ff5 --- /dev/null +++ b/src/components/Timeline/TimelineStep.tsx @@ -0,0 +1,117 @@ +import React, { useState } from 'react'; +import { motion, AnimatePresence } from 'framer-motion'; +import { + Sprout, + Truck, + Store, + ChevronDown, + Building +} from 'lucide-react'; +import TimelineStepDetails from './TimelineStepDetails'; +import { TimelineEvent } from './types'; + +interface TimelineStepProps { + event: TimelineEvent; + index: number; + globalCertifications?: string; +} + +const stageIconMap: Record = { + farmer: Sprout, + transport: Truck, + retailer: Store, + mandi: Building, +}; + +const statusStyles: Record = { + completed: { + dot: "bg-green-500", + badge: "text-green-600 bg-green-100", + }, + pending: { + dot: "bg-gray-400", + badge: "text-gray-600 bg-gray-100", + }, + flagged: { + dot: "bg-red-500", + badge: "text-red-600 bg-red-100", + }, +}; + +const TimelineStep: React.FC = ({ event, index, globalCertifications }) => { + const [open, setOpen] = useState(false); + + // Fallback for icon and styles + const Icon = stageIconMap[event.stage as string] || Store; + const status = event.status || 'completed'; + const styles = statusStyles[status]; + + // Certifications can come from event or global + const certs = event.certifications || globalCertifications; + + return ( + + {/* Timeline Dot */} +
+ +
+ + {/* Card */} +
setOpen(!open)} + > +
+
+

+ {event.stage} +

+ + {status} + +
+ + {/* Arrow Rotation */} + + + +
+ + {/* Expandable Section */} + + {open && ( + + + + )} + +
+
+ ); +}; + +export default TimelineStep; diff --git a/src/components/Timeline/TimelineStepDetails.tsx b/src/components/Timeline/TimelineStepDetails.tsx new file mode 100644 index 0000000..1f07cf5 --- /dev/null +++ b/src/components/Timeline/TimelineStepDetails.tsx @@ -0,0 +1,62 @@ +import React from 'react'; +import { Clock, User, MapPin, Award } from 'lucide-react'; + +interface TimelineStepDetailsProps { + actor: string; + location: string; + timestamp: string; + certifications?: string; + notes?: string; +} + +const TimelineStepDetails: React.FC = ({ + actor, + location, + timestamp, + certifications, + notes, +}) => { + const formatDate = (dateString: string) => + new Date(dateString).toLocaleString("en-US", { + dateStyle: "medium", + timeStyle: "short", + }); + + return ( +
+
+ + Timestamp: + {formatDate(timestamp)} +
+ +
+ + Actor: + {actor} +
+ +
+ + Location: + {location} +
+ + {certifications && ( +
+ + Certifications: + {certifications} +
+ )} + + {notes && ( +
+ {notes} +
+ )} +
+ ); +}; + +export default TimelineStepDetails; diff --git a/src/components/Timeline/index.ts b/src/components/Timeline/index.ts new file mode 100644 index 0000000..89aef85 --- /dev/null +++ b/src/components/Timeline/index.ts @@ -0,0 +1,3 @@ +import Timeline from './Timeline'; +export default Timeline; +export * from './types'; diff --git a/src/components/Timeline/types.ts b/src/components/Timeline/types.ts new file mode 100644 index 0000000..3474cac --- /dev/null +++ b/src/components/Timeline/types.ts @@ -0,0 +1,12 @@ +export type Stage = "farmer" | "transport" | "retailer" | "mandi" | string; +export type Status = "completed" | "pending" | "flagged"; + +export interface TimelineEvent { + stage: Stage; + actor: string; + location: string; + timestamp: string; + status?: Status; + notes?: string; + certifications?: string; +} diff --git a/src/main.tsx b/src/main.tsx index 4438c13..8d7602c 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -2,6 +2,7 @@ import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; import App from './App.tsx'; import './index.css'; +import { AuthProvider } from './context/AuthContext'; import { ThemeProvider } from './context/ThemeContext'; import './i18n/config'; // Initialize i18n import { AuthProvider } from './context/AuthContext.tsx'; diff --git a/src/pages/TrackBatch.tsx b/src/pages/TrackBatch.tsx index d4949d5..04ed54b 100644 --- a/src/pages/TrackBatch.tsx +++ b/src/pages/TrackBatch.tsx @@ -1,6 +1,8 @@ import React, { useState } from 'react'; import { Search, QrCode, Package, Calendar, MapPin, User, FileText } from 'lucide-react'; import { useTranslation } from 'react-i18next'; +import { cropBatchService } from '../services/cropBatchService'; +import Timeline from '../components/Timeline'; import { realCropBatchService } from '../services/realCropBatchService'; // import Timeline from '../components/Timeline'; import QRScanner from '../components/QRScanner'; @@ -193,7 +195,7 @@ const TrackBatch: React.FC = () => { Supply Chain Journey - {/* */} +
{/* QR Code */} diff --git a/src/pages/UpdateBatch.tsx b/src/pages/UpdateBatch.tsx index d76f0dd..535317e 100644 --- a/src/pages/UpdateBatch.tsx +++ b/src/pages/UpdateBatch.tsx @@ -1,5 +1,7 @@ import React, { useState } from 'react'; import { RefreshCw, Search, Package, Clock, User, MapPin } from 'lucide-react'; +import { cropBatchService } from '../services/cropBatchService'; +import Timeline from '../components/Timeline'; import { realCropBatchService } from '../services/realCropBatchService'; // import Timeline from '../components/Timeline'; import { FormSkeleton, BatchInfoSkeleton } from '../components/skeletons'; @@ -179,7 +181,7 @@ const UpdateBatch: React.FC = () => { Supply Chain Timeline - {/* */} +
{/* Update Form */}