Antaram's picture
Upload 40 files
4a53540 verified
import React, { useState, useEffect } from 'react';
import { Link, useLocation } from 'react-router-dom';
import {
Home,
FileInput,
FileOutput,
Users,
Package,
Settings,
Menu,
X,
TrendingUp
} from 'lucide-react';
interface LayoutProps {
children: React.ReactNode;
}
const Layout: React.FC<LayoutProps> = ({ children }) => {
const [isSidebarOpen, setSidebarOpen] = useState(false);
const location = useLocation();
const [pigmiPopup, setPigmiPopup] = useState<{ open: boolean; message: string }>(() => ({
open: false,
message: '',
}));
const getPigmiState = () => {
try {
const stored = localStorage.getItem('pp_pigmi_config');
if (!stored) return null;
const parsed = JSON.parse(stored);
const enabled = typeof parsed.enabled === 'boolean' ? parsed.enabled : false;
const message = typeof parsed.message === 'string' ? parsed.message : '';
const time = typeof parsed.time === 'string' && parsed.time ? parsed.time : '09:00';
const dayOfMonth =
typeof parsed.dayOfMonth === 'number' &&
!Number.isNaN(parsed.dayOfMonth) &&
parsed.dayOfMonth >= 1 &&
parsed.dayOfMonth <= 31
? parsed.dayOfMonth
: 1;
if (!enabled || !message.trim()) return null;
const now = new Date();
const key = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
const lastKey = localStorage.getItem('pp_pigmi_last_shown_month') || '';
const [hh, mm] = String(time).split(':').map((v: string) => Number(v));
const lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate();
const day = Math.max(1, Math.min(lastDay, dayOfMonth));
const scheduled = new Date(now.getFullYear(), now.getMonth(), day, hh || 0, mm || 0, 0, 0);
return {
key,
lastKey,
message: message.trim(),
dueNow: now.getTime() >= scheduled.getTime(),
alreadyShownThisMonth: lastKey === key,
};
} catch (error) {
console.error('Error reading pigmi config', error);
return null;
}
};
const triggerPigmiIfDue = () => {
const pigmi = getPigmiState();
if (!pigmi) return;
if (pigmi.dueNow && !pigmi.alreadyShownThisMonth) {
try {
localStorage.setItem('pp_pigmi_last_shown_month', pigmi.key);
} catch (error) {
console.error('Error saving pigmi month key', error);
}
setPigmiPopup({ open: true, message: pigmi.message });
}
};
useEffect(() => {
triggerPigmiIfDue();
const id = window.setInterval(() => triggerPigmiIfDue(), 30_000);
return () => window.clearInterval(id);
}, []);
const navItems = [
{ path: '/', label: 'डॅशबोर्ड (Home)', icon: Home },
{ path: '/jawaak', label: 'जावक बिल (Sales)', icon: FileOutput },
{ path: '/awaak', label: 'आवक बिल (Purchase)', icon: FileInput },
{ path: '/ledger', label: 'पार्टी लेजर (Ledger)', icon: Users },
{ path: '/stock', label: 'स्टॉक रिपोर्ट (Stock)', icon: Package },
{ path: '/analysis', label: 'अ‍ॅनालिसीस (Analysis)', icon: TrendingUp },
{ path: '/settings', label: 'सेटिंग्स (Settings)', icon: Settings },
];
const isActive = (path: string) => location.pathname === path;
return (
<div className="flex h-screen bg-gray-50 overflow-hidden">
{pigmiPopup.open && (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
<div
className="absolute inset-0 bg-black/40"
onClick={() => setPigmiPopup((p) => ({ ...p, open: false }))}
/>
<div className="relative w-full max-w-md bg-white rounded-xl shadow-2xl border border-gray-100 overflow-hidden">
<div className="p-4 bg-teal-700 text-white flex items-center justify-between">
<div className="font-semibold">Pigmi Reminder</div>
<button
className="text-white/90 hover:text-white text-sm font-medium"
onClick={() => setPigmiPopup((p) => ({ ...p, open: false }))}
>
Close
</button>
</div>
<div className="p-4">
<div className="text-sm text-gray-700 whitespace-pre-wrap">{pigmiPopup.message}</div>
<div className="mt-4 flex justify-end">
<button
className="px-4 py-2 bg-teal-600 text-white rounded-lg text-sm font-medium hover:bg-teal-700"
onClick={() => setPigmiPopup((p) => ({ ...p, open: false }))}
>
OK
</button>
</div>
</div>
</div>
</div>
)}
{/* Mobile Sidebar Overlay */}
{isSidebarOpen && (
<div
className="fixed inset-0 bg-black bg-opacity-50 z-20 lg:hidden"
onClick={() => setSidebarOpen(false)}
/>
)}
{/* Sidebar */}
<aside
className={`fixed lg:static inset-y-0 left-0 w-64 bg-teal-800 text-white transform transition-transform duration-200 ease-in-out z-30 ${isSidebarOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'
}`}
>
<div className="flex items-center justify-between p-4 border-b border-teal-700 h-16">
<div className="flex items-center gap-2">
<TrendingUp className="w-6 h-6 text-teal-300" />
<span className="font-bold text-xl">Mirchi Vyapar</span>
</div>
<button
className="lg:hidden p-1 hover:bg-teal-700 rounded"
onClick={() => setSidebarOpen(false)}
>
<X size={24} />
</button>
</div>
<nav className="p-4 space-y-1">
{navItems.map((item) => (
<Link
key={item.path}
to={item.path}
onClick={() => setSidebarOpen(false)}
className={`flex items-center gap-3 px-4 py-3 rounded-lg transition-colors ${isActive(item.path)
? 'bg-teal-900 text-teal-100 shadow-sm'
: 'text-teal-100 hover:bg-teal-700'
}`}
>
<item.icon size={20} />
<span className="font-medium">{item.label}</span>
</Link>
))}
</nav>
<div className="absolute bottom-0 w-full p-4 border-t border-teal-700 bg-teal-800">
<div className="flex items-center gap-3">
<div className="w-8 h-8 rounded-full bg-teal-600 flex items-center justify-center font-bold">
A
</div>
<div>
<p className="text-sm font-medium">Admin User</p>
<p className="text-xs text-teal-300">Mirchi Mandi</p>
</div>
</div>
</div>
</aside>
{/* Main Content */}
<main className="flex-1 flex flex-col overflow-hidden">
{/* Top Header */}
<header className="h-16 bg-white border-b flex items-center justify-between px-4 lg:px-8">
<button
className="lg:hidden p-2 -ml-2 text-gray-600 hover:bg-gray-100 rounded-lg"
onClick={() => setSidebarOpen(true)}
>
<Menu size={24} />
</button>
<h1 className="text-xl font-semibold text-gray-800 ml-2 lg:ml-0">
{navItems.find(i => isActive(i.path))?.label.split(' (')[0] || 'Mirchi Vyapar'}
</h1>
<div className="flex items-center gap-4">
<span className="text-sm text-gray-500 hidden md:block">
{new Date().toLocaleDateString('mr-IN', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}
</span>
</div>
</header>
{/* Page Content */}
<div className="flex-1 overflow-auto p-4 lg:p-6 pb-20 lg:pb-6">
{children}
</div>
</main>
</div>
);
};
export default Layout;