converted the tabs into screens and fixed the nav

This commit is contained in:
suryac 2026-07-06 15:46:41 +05:30
parent d5e4ebee5d
commit 4990eceada
8 changed files with 156 additions and 68 deletions

View File

@ -2,7 +2,10 @@ import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom'
import { AuthProvider } from './auth/AuthProvider'
import { LoginPage } from './screens/LoginPage'
import { ConsoleLayout } from './screens/ConsoleLayout'
import { WorkflowPage } from './screens/WorkflowPage'
import { OrdersPage } from './screens/OrdersPage'
import { CallsPage } from './screens/CallsPage'
import { StoresPage } from './screens/StoresPage'
import { DailyLogsPage } from './screens/DailyLogsPage'
function App() {
return (
@ -11,8 +14,17 @@ function App() {
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route element={<ConsoleLayout />}>
<Route path="/:tab" element={<WorkflowPage />} />
<Route path="/:tab/:instanceId" element={<WorkflowPage />} />
<Route path="/orders" element={<OrdersPage />} />
<Route path="/orders/:instanceId" element={<OrdersPage />} />
<Route path="/calls" element={<CallsPage />} />
<Route path="/calls/:instanceId" element={<CallsPage />} />
<Route path="/stores" element={<StoresPage />} />
<Route path="/stores/:instanceId" element={<StoresPage />} />
<Route path="/daily" element={<DailyLogsPage />} />
<Route path="/daily/:instanceId" element={<DailyLogsPage />} />
</Route>
<Route path="*" element={<Navigate to="/orders" replace />} />
</Routes>

27
src/screens/CallsPage.tsx Normal file
View File

@ -0,0 +1,27 @@
import { useNavigate, useParams } from 'react-router-dom';
import { Modal } from '../components/reusable';
import { CallsView } from '../components/rv';
import { CallDetail } from '../components/dv';
export function CallsPage() {
const { instanceId } = useParams();
const navigate = useNavigate();
return (
<>
<CallsView onRowClick={(row) => {
const id = row.instance_id as number | string | undefined;
if (id != null) navigate(`/calls/${id}`);
}} />
<Modal
open={instanceId != null}
onClose={() => navigate(`/calls`)}
title={instanceId != null ? `Call #${instanceId}` : undefined}
width="lg"
>
{instanceId != null && <CallDetail instanceId={instanceId} />}
</Modal>
</>
);
}

View File

@ -6,7 +6,7 @@ import { useAuth } from '../auth/context';
import { onAuthErrorAll, orderBookingClient } from '../api/clients';
import { APP_ID } from '../api/config';
import { Button } from '../components/buttons';
import { TABS } from './tabs';
import { SCREENS } from './tabs';
/** Auth-guarded shell: navy top bar + tab nav + routed <Outlet>. */
export function ConsoleLayout() {
@ -25,12 +25,32 @@ export function ConsoleLayout() {
if (!authed) return <Navigate to="/login" replace />;
return (
<div className="min-h-screen bg-app">
<header className="sticky top-0 z-10 flex items-center justify-between gap-3 px-6 h-[60px] bg-navy-grad border-b border-border-navy">
<div className="min-h-screen bg-app flex flex-col">
<header className="sticky top-0 z-10 shrink-0 flex items-center justify-between gap-3 px-6 h-[60px] bg-navy-grad border-b border-border-navy">
<div className="flex flex-col">
<span className="text-md font-extrabold text-on-navy tracking-[-0.01em] leading-none">Krishna Sales</span>
<span className="text-2xs text-on-navy-muted">Field Sales · Sandbox {APP_ID}</span>
</div>
<nav className="flex items-center gap-1 h-full">
{SCREENS.map((t) => {
const Icon = t.icon;
return (
<NavLink
key={t.key}
to={`/${t.key}`}
className={({ isActive }) =>
cn(
'flex items-center gap-1.5 no-underline font-sans text-sm font-medium px-3 py-1.5 rounded-md transition-all duration-150',
isActive ? 'bg-white/10 text-white' : 'text-on-navy-muted hover:text-white hover:bg-white/5',
)
}
>
<Icon size={16} />
{t.label}
</NavLink>
);
})}
</nav>
<div className="flex items-center gap-3">
{user?.name && <span className="text-sm text-on-navy hidden sm:inline">{user.name}</span>}
<Button
@ -47,29 +67,10 @@ export function ConsoleLayout() {
</div>
</header>
<main className="max-w-[1240px] mx-auto p-6 flex flex-col gap-5">
<nav className="flex gap-0.5 bg-slate-100 rounded-md p-[3px] w-fit">
{TABS.map((t) => {
const Icon = t.icon;
return (
<NavLink
key={t.key}
to={`/${t.key}`}
className={({ isActive }) =>
cn(
'inline-flex items-center gap-1.5 no-underline font-sans text-xs font-semibold px-3.5 py-2 rounded-sm transition-all duration-150',
isActive ? 'bg-card text-strong shadow-xs' : 'bg-transparent text-muted hover:text-strong',
)
}
>
<Icon size={14} />
{t.label}
</NavLink>
);
})}
</nav>
<Outlet />
<main className="flex-1 overflow-y-auto p-6">
<div className="max-w-[1240px] w-full mx-auto flex flex-col gap-5">
<Outlet />
</div>
</main>
</div>
);

View File

@ -0,0 +1,27 @@
import { useNavigate, useParams } from 'react-router-dom';
import { Modal } from '../components/reusable';
import { DailyLogsView } from '../components/rv';
import { DailyLogDetail } from '../components/dv';
export function DailyLogsPage() {
const { instanceId } = useParams();
const navigate = useNavigate();
return (
<>
<DailyLogsView onRowClick={(row) => {
const id = row.instance_id as number | string | undefined;
if (id != null) navigate(`/daily/${id}`);
}} />
<Modal
open={instanceId != null}
onClose={() => navigate(`/daily`)}
title={instanceId != null ? `Daily Log #${instanceId}` : undefined}
width="lg"
>
{instanceId != null && <DailyLogDetail instanceId={instanceId} />}
</Modal>
</>
);
}

View File

@ -0,0 +1,27 @@
import { useNavigate, useParams } from 'react-router-dom';
import { Modal } from '../components/reusable';
import { OrdersView } from '../components/rv';
import { OrderDetail } from '../components/dv';
export function OrdersPage() {
const { instanceId } = useParams();
const navigate = useNavigate();
return (
<>
<OrdersView onRowClick={(row) => {
const id = row.instance_id as number | string | undefined;
if (id != null) navigate(`/orders/${id}`);
}} />
<Modal
open={instanceId != null}
onClose={() => navigate(`/orders`)}
title={instanceId != null ? `Order #${instanceId}` : undefined}
width="lg"
>
{instanceId != null && <OrderDetail instanceId={instanceId} />}
</Modal>
</>
);
}

View File

@ -0,0 +1,27 @@
import { useNavigate, useParams } from 'react-router-dom';
import { Modal } from '../components/reusable';
import { StoresView } from '../components/rv';
import { StoreDetail } from '../components/dv';
export function StoresPage() {
const { instanceId } = useParams();
const navigate = useNavigate();
return (
<>
<StoresView onRowClick={(row) => {
const id = row.instance_id as number | string | undefined;
if (id != null) navigate(`/stores/${id}`);
}} />
<Modal
open={instanceId != null}
onClose={() => navigate(`/stores`)}
title={instanceId != null ? `Store #${instanceId}` : undefined}
width="lg"
>
{instanceId != null && <StoreDetail instanceId={instanceId} />}
</Modal>
</>
);
}

View File

@ -1,33 +0,0 @@
import { Navigate, useNavigate, useParams } from 'react-router-dom';
import { Modal } from '../components/reusable';
import { tabByKey } from './tabs';
/** Record view for /:tab, with a deep-linkable detail modal at /:tab/:instanceId. */
export function WorkflowPage() {
const { tab, instanceId } = useParams();
const navigate = useNavigate();
const def = tabByKey(tab);
// Unknown tab → default to orders.
if (!def) return <Navigate to="/orders" replace />;
const { View, Detail, noun, key } = def;
return (
<>
<View onRowClick={(row) => {
const id = row.instance_id as number | string | undefined;
if (id != null) navigate(`/${key}/${id}`);
}} />
<Modal
open={instanceId != null}
onClose={() => navigate(`/${key}`)}
title={instanceId != null ? `${noun} #${instanceId}` : undefined}
width="lg"
>
{instanceId != null && <Detail instanceId={instanceId} />}
</Modal>
</>
);
}

View File

@ -14,10 +14,10 @@ import {
type WiredDetailViewProps,
} from '../components/dv';
export type TabKey = 'orders' | 'calls' | 'stores' | 'daily';
export type ScreenKey = 'orders' | 'calls' | 'stores' | 'daily';
export interface TabDef {
key: TabKey;
export interface ScreenDef {
key: ScreenKey;
label: string;
icon: LucideIcon;
View: (p: WiredRecordViewProps) => React.JSX.Element;
@ -26,13 +26,13 @@ export interface TabDef {
noun: string;
}
export const TABS: TabDef[] = [
export const SCREENS: ScreenDef[] = [
{ key: 'orders', label: 'Orders', icon: ShoppingCart, View: OrdersView, Detail: OrderDetail, noun: 'Order' },
{ key: 'calls', label: 'Calls', icon: Phone, View: CallsView, Detail: CallDetail, noun: 'Call' },
{ key: 'stores', label: 'Stores', icon: Store, View: StoresView, Detail: StoreDetail, noun: 'Store' },
{ key: 'daily', label: 'Daily Logs', icon: ClipboardList, View: DailyLogsView, Detail: DailyLogDetail, noun: 'Daily Log' },
];
export function tabByKey(key: string | undefined): TabDef | undefined {
return TABS.find((t) => t.key === key);
export function screenByKey(key: string | undefined): ScreenDef | undefined {
return SCREENS.find((t) => t.key === key);
}