diff --git a/README.md b/README.md index 3ced04c..5eb208b 100644 --- a/README.md +++ b/README.md @@ -1,50 +1,54 @@ -# Doormile Console — Admin +# Doormile CRM -A modern, corporate logistics console for **Doormile**, a last-mile / courier delivery operation. Built with **React 18 + Vite + Material-UI v5**, themed around the Doormile brand red `#C01227`. +A modern, corporate customer relationship management system for **Doormile**, a last-mile logistics and courier delivery operation. Built with **React 18 + Vite + Material-UI v5**, themed around the Doormile brand red `#C01227`. -## Quick start +## Quick Start ```bash +# Install dependencies npm install -npm run dev # http://localhost:3000 -npm run build # production build → dist/ -npm run preview # preview the production build + +# Run the development server (default port: 5173 or similar) +npm run dev + +# Build the project for production +npm run build + +# Preview the production build locally +npm run preview ``` -## What's inside +## Application Structure & Pages -A clean, data-dense but breathable corporate shell — fixed **red top header** (search, notifications, messages, profile) + **collapsible red sidebar** (260px ↔ 78px icon rail) + light `#FAFAFB` content area — wrapping **27 screens**: +Doormile CRM features a clean, responsive layout consisting of a **collapsible sidebar** and a **header panel**, wrapping the following key modules: -| Area | Screens | -| --- | --- | -| Overview | Dashboard | -| Orders | Orders list · Order Details (tracking + timeline) · Create Order · Create Multiple Orders · Assign Orders | -| Deliveries | Deliveries (expandable products, status tabs) | -| Network | Tenants · Create Client · Customers · Create Customer · Pricing · Riders · Create Rider · Edit Rider | -| Reports | Order Summary · Order Details · Riders Summary · Riders Logs (live map) | -| Finance | Invoices · Invoice Preview (printable A4) · Requests | -| Account | User Profile | -| Auth & states | Login · 404 · 500 · Under Construction · Coming Soon | +| Route Path | Page Component | Description | +| --- | --- | --- | +| `/dashboard` | `Dashboard.jsx` | Overview of registered clients, active contracts, total parcel volume, status donuts, and quick-view charts. | +| `/tenants` | `Tenants.jsx` | Management view for clients, including expandable details (parcel volume, logistics segments, transit routes, and coordinates map link). | +| `/survey` | `Survey.jsx` | Provider survey records mapping competitors, branch locations, and rate-per-kg answers in an aligned grid layout. | +| `/pricing` | `Pricing.jsx` | Pricing matrix slabs and logistics carrier rates management with search and active-provider selectors. | +| `/bookings` | `Bookings.jsx` | Courier bookings creation and management dialogs, along with pricing checks. | +| `/app-users` | `TeamUsers.jsx` | Team member list, user roles configuration, and initial permission setup. | +| `/settings` | `Settings.jsx` | Preferences dashboard covering organization profiles, system notification switches, and security/password updates. | -## Design system +## Design System -- **Brand red** `#C01227` (`lighter #F8E0E3 → darker #7E0B17`), white-on-red header/sidebar. +- **Brand red** `#C01227` (`lighter #F8E0E3` and `darker #7E0B17`) representing the primary action theme color. - **Status palette** — success `#00A854`, warning `#FFBF00`, info `#00A2AE`, error `#F04134`. -- **Public Sans** type scale, 6–10px radii, soft `0 1px 4px` card shadows, and a signature **red glow** on primary CTAs. -- All tuning lives in `src/theme/` (`palette.js`, `typography.js`, `shadows.js`, `componentsOverride.js`). +- **Public Sans** typography scale, clean border-radii (`8px` to `16px`), soft shadows, and subtle responsive layouts (flex-based grid column alignment). +- Theme configuration resides in `src/theme/`. -## Project structure +## Project Folders ``` src/ - theme/ Doormile red theme + MUI component overrides - layout/ MainLayout (red header + collapsible sidebar) · MinimalLayout - menu/ sidebar nav config - components/ PageHeader, StatCard, StatusChip, MainCard, EmptyState, - UserAvatar, MapPlaceholder, Logo, charts/ (Area, Donut) - data/mock.js static demo data powering every screen - pages/ all 27 screens (lazy-loaded) - App.jsx router · main.jsx app entry + components/ Reusable UI widgets (PageHeader, StatCard, StatusChip, EmptyState) + layout/ MainLayout (header + collapsible sidebar) & MinimalLayout + menu/ Sidebar navigation mappings (`navItems.jsx`) + pages/ CRM modules and page components (dashboard, tenants, survey, pricing, bookings, team, settings) + theme/ Doormile custom theme configurations and component style overrides + utils/ API fetching wrapper client + App.jsx App router and route definitions + main.jsx Application entry point ``` - -Charts and maps are dependency-free SVG placeholders (`components/charts`, `MapPlaceholder`) — swap in Recharts / Leaflet / Google Maps when wiring real data. All data is mocked in `src/data/mock.js`; there is no backend. diff --git a/src/App.jsx b/src/App.jsx index ad72721..139d792 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -37,7 +37,7 @@ export default function App() { import('@/pages/bookings/Bookings'))} /> - import('@/pages/team/TeamUsers'))} /> + import('@/pages/team/TeamUsers'))} /> import('@/pages/Settings'))} /> diff --git a/src/layout/MainLayout/Sidebar.jsx b/src/layout/MainLayout/Sidebar.jsx index 102d543..b0c903c 100644 --- a/src/layout/MainLayout/Sidebar.jsx +++ b/src/layout/MainLayout/Sidebar.jsx @@ -158,13 +158,6 @@ export default function Sidebar({ open, mobileOpen, onMobileClose, isMobile }) { ))} - {expanded && ( - - - Doormile CRM v1.0 - - - )} ); diff --git a/src/menu/navItems.jsx b/src/menu/navItems.jsx index 6a4f4b8..10e706c 100644 --- a/src/menu/navItems.jsx +++ b/src/menu/navItems.jsx @@ -15,7 +15,7 @@ const navItems = [ { id: 'survey', title: 'Providers', url: '/survey', icon: DashboardOutlinedIcon }, { id: 'pricing', title: 'Pricing Matrix', url: '/pricing', icon: AttachMoneyOutlinedIcon }, { id: 'bookings', title: 'Bookings', url: '/bookings', icon: DashboardOutlinedIcon }, - { id: 'team-users', title: 'App Users', url: '/team-users', icon: GroupsOutlinedIcon } + { id: 'team-users', title: 'App Users', url: '/app-users', icon: GroupsOutlinedIcon } ] }, { diff --git a/src/pages/bookings/Bookings.jsx b/src/pages/bookings/Bookings.jsx index 5a5dc61..afa4c71 100644 --- a/src/pages/bookings/Bookings.jsx +++ b/src/pages/bookings/Bookings.jsx @@ -1,15 +1,21 @@ import { useState, useEffect } from 'react'; import { - Card, Box, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, + Card, Box, Stack, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography, TextField, Button, Dialog, DialogTitle, DialogContent, DialogActions, - Grid, IconButton, CircularProgress, Chip, MenuItem, InputAdornment, alpha, Autocomplete, Checkbox, FormControlLabel, Tabs, Tab, Snackbar, Alert + Grid, IconButton, CircularProgress, Chip, MenuItem, InputAdornment, TablePagination, Autocomplete, Checkbox, FormControlLabel, Tabs, Tab, Snackbar, Alert } from '@mui/material'; import AddIcon from '@mui/icons-material/Add'; import SearchIcon from '@mui/icons-material/Search'; import CloseIcon from '@mui/icons-material/Close'; import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; +import LocalShippingOutlinedIcon from '@mui/icons-material/LocalShippingOutlined'; +import PhoneIphoneOutlinedIcon from '@mui/icons-material/PhoneIphoneOutlined'; +import DesktopWindowsOutlinedIcon from '@mui/icons-material/DesktopWindowsOutlined'; +import PendingActionsOutlinedIcon from '@mui/icons-material/PendingActionsOutlined'; import PageHeader from '@/components/PageHeader'; +import StatCard from '@/components/StatCard'; +import EmptyState from '@/components/EmptyState'; import { fetchClients } from '@/utils/apiClient'; import ClientFormDialog from '../tenants/ClientFormDialog'; @@ -427,6 +433,8 @@ export default function Bookings() { const [search, setSearch] = useState(''); const [sourceFilter, setSourceFilter] = useState('All'); const [formOpen, setFormOpen] = useState(false); + const [page, setPage] = useState(0); + const [rpp, setRpp] = useState(10); const loadData = async () => { setLoading(true); @@ -459,29 +467,62 @@ export default function Bookings() { return matchesSearch && matchesSource; }); + const paged = displayBookings.slice(page * rpp, page * rpp + rpp); + + const stats = { + total: bookings.length, + app: bookings.filter((b) => b.bookingsource === 'Customer_App').length, + crm: bookings.filter((b) => b.bookingsource === 'CRM_Console').length, + pending: bookings.filter((b) => b.status === 'Pending_Pickup').length + }; + return ( - + <> - - - setSearch(e.target.value)} - size="small" - InputProps={{ - startAdornment: , - sx: { borderRadius: 3, bgcolor: alpha('#e2e8f0', 0.4) } - }} - /> - + + + + + + + + + `linear-gradient(90deg, ${theme.palette.primary.lighter}66 0%, ${theme.palette.background.paper} 70%)` + }} + > + + + + + All Bookings + Track and manage every shipment booking + - - setSourceFilter(val)} aria-label="booking source filter tabs"> + + { setSearch(e.target.value); setPage(0); }} + sx={{ width: { xs: '100%', md: 300 } }} + InputProps={{ startAdornment: }} + /> + + {!loading && ( + + {displayBookings.length} {displayBookings.length === 1 ? 'booking' : 'bookings'} + + )} + + + + + { setSourceFilter(val); setPage(0); }} aria-label="booking source filter tabs"> @@ -489,58 +530,60 @@ export default function Bookings() { {loading ? ( - + + ) : displayBookings.length === 0 ? ( + ) : ( - - Booking No - Delivery City - Provider - Source - Price - Status + + Booking No + Delivery City + Provider + Source + Price + Status - {displayBookings.length === 0 ? ( - No bookings found. - ) : ( - displayBookings.map(b => ( - - {b.bookingno} - {b.deliverycity || b.deliverypincode || 'N/A'} - - {b.providercompany ? ( - - ) : 'N/A'} - - - - - - {b.serviceoptions && b.serviceoptions.length > 0 ? `₹${b.serviceoptions[0].estimatedprice}` : 'N/A'} - - - - - - )) - )} + {paged.map(b => ( + + {b.bookingno} + {b.deliverycity || b.deliverypincode || 'N/A'} + + {b.providercompany ? ( + + ) : 'N/A'} + + + + + + {b.serviceoptions && b.serviceoptions.length > 0 ? `₹${b.serviceoptions[0].estimatedprice}` : 'N/A'} + + + + + + ))}
)} + setPage(p)} + rowsPerPage={rpp} onRowsPerPageChange={(e) => { setRpp(+e.target.value); setPage(0); }} rowsPerPageOptions={[5, 10, 25]} + />
- setFormOpen(false)} + setFormOpen(false)} onSave={() => { loadData(); }} clients={clients} surveys={surveys} /> -
+ ); } diff --git a/src/pages/pricing/Pricing.jsx b/src/pages/pricing/Pricing.jsx index 6278978..6a65312 100644 --- a/src/pages/pricing/Pricing.jsx +++ b/src/pages/pricing/Pricing.jsx @@ -2,7 +2,7 @@ import { useState, useEffect } from 'react'; import { Card, Box, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography, TextField, InputAdornment, CircularProgress, MenuItem, - Select, FormControl, OutlinedInput, alpha, Avatar, Chip, Stack, Collapse, Button, + Select, FormControl, OutlinedInput, Avatar, Chip, Stack, Collapse, Button, Dialog, DialogTitle, DialogContent, DialogActions, IconButton, Grid, Tooltip, Alert, Snackbar } from '@mui/material'; import AddIcon from '@mui/icons-material/Add'; @@ -149,7 +149,7 @@ function PricingFormDialog({ open, onClose, onSave, initialData, isCell = false, {isCell ? ( - + UPDATING RATE FOR {formData.company} {formData.weight_slab} · {formData.zone || formData.service_type} @@ -199,7 +199,7 @@ function PricingFormDialog({ open, onClose, onSave, initialData, isCell = false, - @@ -303,154 +303,111 @@ export default function Pricing() { : allProviderSlabs; return ( - + <> - - - {/* Modern Premium Control Panel */} - - - - - - - - Active Logistics Provider - - - - - - + - + {/* Control Panel */} + + setSearchQuery(e.target.value)} - InputProps={{ - startAdornment: , - sx: { - borderRadius: 50, - bgcolor: alpha('#e2e8f0', 0.3), - '& fieldset': { border: 'none' }, - '&:hover': { bgcolor: alpha('#e2e8f0', 0.5) }, - '&.Mui-focused': { bgcolor: '#fff', '& fieldset': { border: '2px solid', borderColor: 'primary.main' } } - } - }} + sx={{ width: { xs: '100%', md: 300 } }} + InputProps={{ startAdornment: }} /> - - - + {/* Minimalist Collapsible Provider Profile Widget */} {selectedProvider && providerSurveys.length > 0 && ( - setLocationsOpen(!locationsOpen)} > - + {/* Avatar & Title */} - - + + {(selectedProvider || 'A')[0].toUpperCase()} LOGISTICS PARTNER {selectedProvider} - + {providerSurveys.length} Registered Location{providerSurveys.length !== 1 && 's'} - + {/* Badges & Action */} - - - + + + -