udpates on the ui changesand api integration
This commit is contained in:
136
src/App.tsx
136
src/App.tsx
@@ -9,7 +9,6 @@ import {
|
||||
Truck,
|
||||
Sliders,
|
||||
Calendar,
|
||||
AlertTriangle,
|
||||
FileCheck,
|
||||
Building,
|
||||
CheckCircle2,
|
||||
@@ -42,9 +41,54 @@ import ReportsView from './components/ReportsView';
|
||||
import InventoryView from './components/InventoryView';
|
||||
import SettingsView from './components/SettingsView';
|
||||
import StoreDetailView from './components/StoreDetailView';
|
||||
import LoginView from './components/LoginView';
|
||||
import UserStorePage from './components/UserStorePage';
|
||||
import AwaitingApi from './components/AwaitingApi';
|
||||
import type { AuthUser } from './services/auth';
|
||||
import ragulStoreCover from './assets/images/store_front_view_1780299351800.png';
|
||||
|
||||
const AUTH_STORAGE_KEY = 'nearledaily.auth';
|
||||
|
||||
/** Rehydrate the signed-in user from localStorage (keeps the session across refreshes). */
|
||||
function loadStoredUser(): AuthUser | null {
|
||||
try {
|
||||
const raw = localStorage.getItem(AUTH_STORAGE_KEY);
|
||||
if (!raw) return null;
|
||||
const parsed = JSON.parse(raw) as AuthUser;
|
||||
return parsed && typeof parsed === 'object' && (parsed.role === 'admin' || parsed.role === 'user')
|
||||
? parsed
|
||||
: null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
// Auth gate — null = signed out (show login). Admin → full dashboard, user →
|
||||
// their single store console. The verified user is persisted to localStorage so
|
||||
// a refresh keeps the session (rehydrated via the lazy initializer); authRole
|
||||
// is derived from it so the two can't drift out of sync.
|
||||
const [authUser, setAuthUser] = useState<AuthUser | null>(() => loadStoredUser());
|
||||
const authRole = authUser?.role ?? null;
|
||||
|
||||
// Persist (or clear) the session whenever the signed-in user changes.
|
||||
useEffect(() => {
|
||||
try {
|
||||
if (authUser) localStorage.setItem(AUTH_STORAGE_KEY, JSON.stringify(authUser));
|
||||
else localStorage.removeItem(AUTH_STORAGE_KEY);
|
||||
} catch {
|
||||
/* storage unavailable (private mode / quota) — session just won't persist */
|
||||
}
|
||||
}, [authUser]);
|
||||
|
||||
const handleLogin = (user: AuthUser) => setAuthUser(user);
|
||||
|
||||
const currentUser = {
|
||||
name: authUser?.name || 'Account',
|
||||
role: authRole === 'admin' ? 'Operations Admin' : 'Team Member',
|
||||
email: authUser?.email || '—',
|
||||
};
|
||||
|
||||
// Navigation indicators states
|
||||
const [currentSection, setCurrentSection] = useState<MainSection>('dashboard');
|
||||
const [selectedStore, setSelectedStore] = useState<{ locationid?: number; name: string; zone: string; deliveries: number; sales: string; orders?: number; staff: string; color: string; status: string } | null>(null);
|
||||
@@ -176,35 +220,53 @@ export default function App() {
|
||||
const [showCalendarModal, setShowCalendarModal] = useState(false);
|
||||
|
||||
// Callback action triggers
|
||||
const handleNewReport = () => {
|
||||
setCurrentSection('reports');
|
||||
alert('System routed back to reports dashboard interface. Select product item metadata matrices.');
|
||||
};
|
||||
|
||||
const handleHelp = () => {
|
||||
alert('nearledaily User Manual & Documentation Center linked successfully. Contact Coimbatore regional IT hub desk for urgent escalations.');
|
||||
};
|
||||
|
||||
const handleLogout = () => {
|
||||
const ok = window.confirm('Are you sure you want to terminate this active secure session?');
|
||||
if (ok) {
|
||||
alert('Secure session suspended. Page reloading and restarting database state simulation.');
|
||||
window.location.reload();
|
||||
}
|
||||
};
|
||||
const handleLogout = () => setAuthUser(null);
|
||||
|
||||
// Define secondary sections (Stores, Logistics, Staffing, Settings) within main body
|
||||
const renderSecondarySection = () => {
|
||||
switch (currentSection) {
|
||||
case 'stores':
|
||||
if (selectedStore) {
|
||||
case 'stores': {
|
||||
// A single-store merchant has no branches, so skip the one-card registry
|
||||
// and open that store directly. Add a branch (2+ stores) and the grid
|
||||
// returns automatically. Clicking a card in multi-store mode also opens
|
||||
// the console, with a Back-to-registry button.
|
||||
const isSoleStore = !selectedStore && storesList.length === 1;
|
||||
const activeStore = selectedStore ?? (isSoleStore ? storesList[0] : null);
|
||||
|
||||
if (activeStore) {
|
||||
return (
|
||||
<StoreDetailView
|
||||
store={selectedStore}
|
||||
onBack={() => setSelectedStore(null)}
|
||||
/>
|
||||
<div className="space-y-md animate-in fade-in duration-300">
|
||||
{isSoleStore && (
|
||||
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4 pb-4 border-b border-zinc-205">
|
||||
<div>
|
||||
<h1 className="font-sans font-bold text-2xl tracking-tight text-[#0f172a]">
|
||||
Store Console
|
||||
</h1>
|
||||
<p className="text-zinc-500 font-sans text-xs mt-1">
|
||||
This merchant operates a single store. Add a branch to manage multiple outlets.
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowAddStoreModal(true)}
|
||||
className="bg-[#581c87] text-white px-5 py-2.5 rounded-lg text-xs font-bold uppercase tracking-wider flex items-center justify-center gap-2 cursor-pointer hover:bg-purple-800 transition shadow-sm self-start md:self-auto"
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
Add Retail Outlet Node
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
<StoreDetailView
|
||||
store={activeStore}
|
||||
onBack={selectedStore ? () => setSelectedStore(null) : undefined}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-lg animate-in fade-in duration-300">
|
||||
{/* Simple and elegant premium header */}
|
||||
@@ -468,6 +530,7 @@ export default function App() {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
case 'settings':
|
||||
return <SettingsView tenantId={FIESTA_TENANT_ID} />;
|
||||
@@ -477,6 +540,17 @@ export default function App() {
|
||||
}
|
||||
};
|
||||
|
||||
// ── Auth gate ──────────────────────────────────────────────────────────────
|
||||
// Signed out → login screen. User role → their single allocated store console
|
||||
// (scoped by applocationid). Only the admin role reaches the full operations
|
||||
// dashboard with the all-stores registry below.
|
||||
if (authRole === null) {
|
||||
return <LoginView onLogin={handleLogin} />;
|
||||
}
|
||||
if (authRole === 'user' && authUser) {
|
||||
return <UserStorePage onLogout={handleLogout} user={authUser} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[#f8fafc] text-[#0f172a] font-sans antialiased">
|
||||
{/* Navbar segment */}
|
||||
@@ -486,9 +560,9 @@ export default function App() {
|
||||
isCoimbatoreView={isCoimbatoreView}
|
||||
onToggleSidebar={() => setSidebarOpen((prev) => !prev)}
|
||||
isSidebarOpen={sidebarOpen}
|
||||
onNewReportClick={handleNewReport}
|
||||
onHelpClick={handleHelp}
|
||||
onLogoutClick={handleLogout}
|
||||
profile={currentUser}
|
||||
/>
|
||||
|
||||
{/* Main Container workspace layout splits */}
|
||||
@@ -556,28 +630,10 @@ export default function App() {
|
||||
|
||||
<div className="p-md space-y-md overflow-y-auto flex-1">
|
||||
<p className="text-zinc-500 leading-relaxed font-semibold">
|
||||
Automated compliance summaries are scheduled to generate and export on the following dates:
|
||||
Automated compliance summaries will appear here once scheduled-report exports are available.
|
||||
</p>
|
||||
|
||||
<div className="divide-y divide-[#f1f5f9] select-none text-[11px]">
|
||||
<div className="py-2 flex justify-between items-center">
|
||||
<span className="font-semibold text-zinc-700">Monthly Assortment Audit Ledger</span>
|
||||
<span className="font-mono text-[#581c87] font-bold">Oct 31, 2023</span>
|
||||
</div>
|
||||
<div className="py-2 flex justify-between items-center">
|
||||
<span className="font-semibold text-zinc-700">Daily Regional Turnover Sheet</span>
|
||||
<span className="text-emerald-600 font-bold">Everyday 23:59 (GMT)</span>
|
||||
</div>
|
||||
<div className="py-2 flex justify-between items-center">
|
||||
<span className="font-semibold text-zinc-700">Q4 Outlook Forecast Draft</span>
|
||||
<span className="font-mono text-zinc-500 font-bold">Nov 15, 2023</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-sm bg-amber-50 border border-amber-100 rounded-lg flex gap-sm text-amber-900 font-medium">
|
||||
<AlertTriangle size={16} className="shrink-0 mt-0.5" />
|
||||
<span>Next automated sync will occur at standard local closing hour thresholds.</span>
|
||||
</div>
|
||||
<AwaitingApi label="Scheduled reports" api="[R13]" />
|
||||
</div>
|
||||
|
||||
<div className="p-sm bg-[#f8fafc] border-t border-[#e2e8f0] flex justify-end shrink-0">
|
||||
|
||||
Reference in New Issue
Block a user