diff --git a/Enquiry.xlsx b/Enquiry.xlsx new file mode 100644 index 0000000..cce9e7d Binary files /dev/null and b/Enquiry.xlsx differ diff --git a/scratch.txt b/scratch.txt new file mode 100644 index 0000000..c86c3f3 --- /dev/null +++ b/scratch.txt @@ -0,0 +1 @@ +none \ No newline at end of file diff --git a/src/App.jsx b/src/App.jsx index 6a7a428..ad72721 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -30,6 +30,12 @@ export default function App() { import('@/pages/Dashboard'))} /> import('@/pages/tenants/Tenants'))} /> + + import('@/pages/survey/Survey.jsx'))} /> + + import('@/pages/pricing/Pricing.jsx'))} /> + + import('@/pages/bookings/Bookings'))} /> import('@/pages/team/TeamUsers'))} /> diff --git a/src/components/AuthGuard.jsx b/src/components/AuthGuard.jsx index 425f39e..011c045 100644 --- a/src/components/AuthGuard.jsx +++ b/src/components/AuthGuard.jsx @@ -1,9 +1,40 @@ -import { Navigate, Outlet } from 'react-router-dom'; +import { useState, useEffect, useRef } from 'react'; +import { Navigate, Outlet, useNavigate } from 'react-router-dom'; + +const INACTIVITY_LIMIT_MS = 10 * 60 * 1000; // 10 minutes export default function AuthGuard({ children }) { - const token = localStorage.getItem('auth_token'); - - if (!token) { + const navigate = useNavigate(); + const loggedIn = localStorage.getItem('logged_in'); + const timerRef = useRef(null); + + useEffect(() => { + if (!loggedIn) return; + + const logout = () => { + localStorage.removeItem('logged_in'); + localStorage.removeItem('auth_token'); + localStorage.removeItem('user_data'); + navigate('/login', { replace: true }); + }; + + const resetTimer = () => { + if (timerRef.current) clearTimeout(timerRef.current); + timerRef.current = setTimeout(logout, INACTIVITY_LIMIT_MS); + }; + + const events = ['mousemove', 'keydown', 'scroll', 'click']; + + events.forEach(event => window.addEventListener(event, resetTimer)); + resetTimer(); // Start the timer initially + + return () => { + if (timerRef.current) clearTimeout(timerRef.current); + events.forEach(event => window.removeEventListener(event, resetTimer)); + }; + }, [loggedIn, navigate]); + + if (!loggedIn) { return ; } diff --git a/src/components/Logo.jsx b/src/components/Logo.jsx index 723d4bd..cb3326b 100644 --- a/src/components/Logo.jsx +++ b/src/components/Logo.jsx @@ -42,8 +42,8 @@ export default function Logo({ onDark = false, compact = false, height = 26, sx height, width: 'auto', display: 'block', - // The asset is white; on light surfaces recolour it to near-black so it stays visible. - filter: onDark ? 'none' : 'brightness(0) saturate(100%)' + // The asset is white; on light surfaces recolour it to Doormile Red (#C01227) + filter: onDark ? 'none' : 'brightness(0) saturate(100%) invert(15%) sepia(93%) saturate(5437%) hue-rotate(346deg) brightness(81%) contrast(92%)' }} /> diff --git a/src/layout/MainLayout/Header.jsx b/src/layout/MainLayout/Header.jsx index 63d7e99..f25b3d2 100644 --- a/src/layout/MainLayout/Header.jsx +++ b/src/layout/MainLayout/Header.jsx @@ -1,4 +1,4 @@ -import { useState, useRef } from 'react'; +import { useState, useRef, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { AppBar, @@ -26,7 +26,8 @@ import LogoutIcon from '@mui/icons-material/Logout'; import Logo from '@/components/Logo'; import UserAvatar from '@/components/UserAvatar'; -import { fetchPoints, COLLECTIONS } from '@/utils/qdrant'; +import { fetchClients, fetchUsers } from '@/utils/apiClient'; +import { toClient } from '@/utils/mappers'; const RED = '#C01227'; @@ -34,25 +35,50 @@ export default function Header({ onToggle }) { const navigate = useNavigate(); const [account, setAccount] = useState(null); const [search, setSearch] = useState(''); + + // Read user from localStorage + let storedUserObj = { name: 'Admin', role: 'Operations Admin', id: 0 }; + try { + const storedUser = localStorage.getItem('user'); + if (storedUser) { + storedUserObj = JSON.parse(storedUser); + } + } catch(e) {} + + const [activeUserName, setActiveUserName] = useState(storedUserObj.name || 'Admin'); + + const displayRole = storedUserObj.role === 'admin' ? 'Administrator' : + storedUserObj.role === 'manager' ? 'Manager' : + storedUserObj.role === 'executive' ? 'Executive' : 'Operations Admin'; + const displayInitial = activeUserName.charAt(0).toUpperCase(); - // Live client search for the top bar. const searchRef = useRef(null); const [clients, setClients] = useState([]); const [loadedClients, setLoadedClients] = useState(false); const [loadingClients, setLoadingClients] = useState(false); const [openResults, setOpenResults] = useState(false); + // Fetch users to get the real name if the login payload didn't have it + useEffect(() => { + fetchUsers().then((users) => { + if (storedUserObj.email) { + // Strictly use email to match, as stored ID might belong to the doormile_auth table instead of appusers table + const matchingUser = users.find(u => u.email === storedUserObj.email); + if (matchingUser && matchingUser.first_name) { + setActiveUserName(matchingUser.first_name); + // Also update localStorage so it's fresh + const updatedUser = { ...storedUserObj, name: matchingUser.first_name }; + localStorage.setItem('user', JSON.stringify(updatedUser)); + } + } + }).catch(console.error); + }, []); + const ensureClients = () => { if (loadedClients || loadingClients) return; setLoadingClients(true); - fetchPoints(COLLECTIONS.clients) - .then((points) => setClients(points.map((p) => ({ - id: p.id, - name: p.payload?.name || '—', - city: p.payload?.city || '', - businessType: p.payload?.businessType || '', - phone: p.payload?.phone || '' - })))) + fetchClients() + .then((points) => setClients(points.map(toClient))) .catch(() => {}) .finally(() => { setLoadedClients(true); setLoadingClients(false); }); }; @@ -84,24 +110,22 @@ export default function Header({ onToggle }) { t.zIndex.drawer + 1, boxShadow: '0 1px 0 rgba(0,0,0,0.06)' }} + sx={{ bgcolor: '#fff', color: 'grey.800', zIndex: (t) => t.zIndex.drawer + 1, borderBottom: 1, borderColor: 'divider' }} > - {/* Brand wordmark — left side */} navigate('/dashboard')} sx={{ display: 'flex', alignItems: 'center', cursor: 'pointer' }} > - + - {/* Search — live client lookup */} setOpenResults(false)}> - + { ensureClients(); if (search.trim()) setOpenResults(true); }} placeholder="Search clients…" - sx={{ color: '#fff', fontSize: '0.875rem', flex: 1, '&::placeholder': { color: '#fff' } }} - inputProps={{ style: { color: '#fff' }, 'aria-label': 'search' }} + sx={{ color: 'grey.800', fontSize: '0.875rem', flex: 1, '&::placeholder': { color: 'grey.500' } }} + inputProps={{ 'aria-label': 'search' }} /> @@ -171,20 +195,19 @@ export default function Header({ onToggle }) { setAccount(e.currentTarget)} - sx={{ display: 'flex', alignItems: 'center', gap: 1, ml: 0.5, cursor: 'pointer', py: 0.5, px: 0.5, borderRadius: 2, '&:hover': { bgcolor: alpha('#fff', 0.14) } }} + sx={{ display: 'flex', alignItems: 'center', gap: 1, ml: 0.5, cursor: 'pointer', py: 0.5, px: 0.5, borderRadius: 2, '&:hover': { bgcolor: 'grey.100' } }} > - A + {displayInitial} - - Admin + + {activeUserName} - - Operations Admin + + {displayRole} - {/* Account dropdown */} - A + {displayInitial} - Admin - Operations Admin + {activeUserName} + {displayRole} @@ -208,7 +231,7 @@ export default function Header({ onToggle }) { Settings - { setAccount(null); navigate('/login'); }} sx={{ color: 'error.main' }}> + { setAccount(null); localStorage.removeItem('auth_token'); navigate('/login'); }} sx={{ color: 'error.main' }}> Logout diff --git a/src/layout/MainLayout/Sidebar.jsx b/src/layout/MainLayout/Sidebar.jsx index 5a9f845..102d543 100644 --- a/src/layout/MainLayout/Sidebar.jsx +++ b/src/layout/MainLayout/Sidebar.jsx @@ -10,7 +10,8 @@ import { Typography, Collapse, Tooltip, - Toolbar + Toolbar, + alpha } from '@mui/material'; import ExpandLess from '@mui/icons-material/ExpandLess'; import ExpandMore from '@mui/icons-material/ExpandMore'; @@ -37,13 +38,15 @@ function NavLeaf({ item, open, active, depth = 0, onClick }) { px: open ? 1.5 : 0, justifyContent: open ? 'flex-start' : 'center', borderRadius: 2, - color: 'rgba(255,255,255,0.78)', - '& .MuiListItemIcon-root': { color: 'inherit' }, - '&:hover': { bgcolor: 'rgba(255,255,255,0.12)', color: '#fff' }, + color: active ? RED : 'grey.700', + '& .MuiListItemIcon-root': { color: active ? RED : 'grey.500' }, + '&:hover': { bgcolor: alpha(RED, 0.04), color: RED, '& .MuiListItemIcon-root': { color: RED } }, '&.Mui-selected': { - bgcolor: 'rgba(255,255,255,0.18)', - color: '#fff', - '&:hover': { bgcolor: 'rgba(255,255,255,0.22)' } + bgcolor: alpha(RED, 0.08), + color: RED, + '& .MuiListItemIcon-root': { color: RED }, + borderLeft: open ? `4px solid ${RED}` : 'none', + '&:hover': { bgcolor: alpha(RED, 0.12) } } }} > @@ -80,9 +83,9 @@ export default function Sidebar({ open, mobileOpen, onMobileClose, isMobile }) { }; const content = ( - + - + {navItems.map((grp) => ( @@ -90,7 +93,7 @@ export default function Sidebar({ open, mobileOpen, onMobileClose, isMobile }) { {expanded && ( {grp.group} @@ -115,12 +118,13 @@ export default function Sidebar({ open, mobileOpen, onMobileClose, isMobile }) { px: expanded ? 1.5 : 0, justifyContent: expanded ? 'flex-start' : 'center', borderRadius: 2, - color: childActive ? '#fff' : 'rgba(255,255,255,0.78)', - bgcolor: childActive && !opened ? 'rgba(255,255,255,0.12)' : 'transparent', - '&:hover': { bgcolor: 'rgba(255,255,255,0.12)', color: '#fff' } + color: childActive ? RED : 'grey.700', + bgcolor: childActive && !opened ? alpha(RED, 0.08) : 'transparent', + '& .MuiListItemIcon-root': { color: childActive ? RED : 'grey.500' }, + '&:hover': { bgcolor: alpha(RED, 0.04), color: RED, '& .MuiListItemIcon-root': { color: RED } } }} > - + {expanded && ( @@ -155,8 +159,8 @@ 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 8f9f657..6a4f4b8 100644 --- a/src/menu/navItems.jsx +++ b/src/menu/navItems.jsx @@ -2,6 +2,7 @@ import DashboardOutlinedIcon from '@mui/icons-material/DashboardOutlined'; import ApartmentOutlinedIcon from '@mui/icons-material/ApartmentOutlined'; import GroupsOutlinedIcon from '@mui/icons-material/GroupsOutlined'; import SettingsOutlinedIcon from '@mui/icons-material/SettingsOutlined'; +import AttachMoneyOutlinedIcon from '@mui/icons-material/AttachMoneyOutlined'; // ==============================|| DOORMILE - SIDEBAR NAV CONFIG ||============================== // @@ -11,6 +12,9 @@ const navItems = [ items: [ { id: 'dashboard', title: 'Dashboard', url: '/dashboard', icon: DashboardOutlinedIcon }, { id: 'tenants', title: 'Clients', url: '/tenants', icon: ApartmentOutlinedIcon }, + { 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 } ] }, diff --git a/src/pages/Dashboard.jsx b/src/pages/Dashboard.jsx index 3c95b0c..088194e 100644 --- a/src/pages/Dashboard.jsx +++ b/src/pages/Dashboard.jsx @@ -19,42 +19,14 @@ import StatusChip from '@/components/StatusChip'; import DonutChart from '@/components/charts/DonutChart'; import UserAvatar from '@/components/UserAvatar'; import EmptyState from '@/components/EmptyState'; -import { fetchPoints, COLLECTIONS } from '@/utils/qdrant'; -import bgImage from '@/assets/premium_logistics_bg.png'; - -const titleCase = (s) => - String(s || '').replace(/[_-]+/g, ' ').replace(/([a-z\d])([A-Z])/g, '$1 $2').replace(/\b\w/g, (c) => c.toUpperCase()).trim(); +import { fetchClients, fetchUsers } from '@/utils/apiClient'; +import { toClient, toUser } from '@/utils/mappers'; +import { titleCase } from '@/utils/format'; const STATUS_COLOR = { newclient: '#00A2AE', contacted: '#FFBF00', onboarded: '#00A854', lost: '#F04134' }; const statusColor = (s) => STATUS_COLOR[String(s || '').toLowerCase()] || '#8C8C8C'; const BAR_COLORS = ['#C01227', '#00A2AE', '#00A854', '#FFBF00', '#9E0E20', '#8C8C8C', '#D6515C']; -const generateLogicalId = (id) => { - const str = String(id).replace(/[^a-zA-Z0-9]/g, '').toUpperCase(); - return 'CLI-' + str.substring(0, 6).padStart(6, '0'); -}; - -function toClient(point) { - const p = point.payload || {}; - return { - id: point.id, - logicalId: generateLogicalId(point.id), - name: p.name || '—', - businessType: p.businessType || '', - city: p.city || '', - businessState: p.businessState || '', - status: p.status || 'unknown', - parcelVolume: Number(p.parcelVolume) || 0, - activeContracts: Number(p.activeContracts) || 0, - lastUpdated: p.lastUpdated || '' - }; -} -function toUser(point) { - const p = point.payload || {}; - return { id: point.id, name: p.name || '—', email: p.email || '', role: p.role || 'unknown' }; -} - -// Card with a tinted icon header band. function Panel({ icon: Icon, title, action, color = 'primary', noPadding = false, children }) { return ( { setLoading(true); setError(null); - Promise.all([fetchPoints(COLLECTIONS.clients), fetchPoints(COLLECTIONS.teamUsers)]) + Promise.all([fetchClients(), fetchUsers()]) .then(([cs, us]) => { - setClients(cs.map(toClient)); - setTeam(us.map(toUser)); + setClients((cs || []).map(toClient)); + setTeam((us || []).map(toUser)); }) .catch((e) => setError(e.message || 'Failed to load dashboard data')) .finally(() => setLoading(false)); diff --git a/src/pages/Settings.jsx b/src/pages/Settings.jsx index c5253f5..76b1a4c 100644 --- a/src/pages/Settings.jsx +++ b/src/pages/Settings.jsx @@ -24,7 +24,7 @@ const TIMEZONES = ['Asia/Kolkata (IST)', 'Asia/Dubai (GST)', 'UTC', 'America/New const LANGUAGES = ['English', 'हिन्दी (Hindi)', 'العربية (Arabic)']; const INITIAL_GENERAL = { - orgName: 'Doormile Logistics Pvt. Ltd.', + orgName: 'Doormile Technologies', supportEmail: 'support@doormile.in', contact: '+91 63749 46729', timezone: TIMEZONES[0], diff --git a/src/pages/auth/Login.jsx b/src/pages/auth/Login.jsx index 484b6a3..352ef34 100644 --- a/src/pages/auth/Login.jsx +++ b/src/pages/auth/Login.jsx @@ -20,11 +20,31 @@ import VisibilityOff from '@mui/icons-material/VisibilityOff'; import Logo from '@/components/Logo'; import bgImage from '../../assets/mid-mile-approach.jpg'; +import { loginAdmin } from '@/utils/apiClient'; + export default function Login() { const navigate = useNavigate(); const [show, setShow] = useState(false); const [auth, setAuth] = useState(''); const [pwd, setPwd] = useState(''); + const [error, setError] = useState(''); + + const handleLogin = async () => { + try { + setError(''); + const data = await loginAdmin(auth, pwd); + localStorage.setItem('logged_in', 'true'); + localStorage.setItem('auth_token', data.token); + localStorage.setItem('user', JSON.stringify(data.user)); + navigate('/dashboard'); + } catch (err) { + setError(err.message || 'Invalid email or password'); + } + }; + + const handleKeyDown = (e) => { + if (e.key === 'Enter') handleLogin(); + }; return ( - Auth Name + Email setAuth(e.target.value)} + onKeyDown={handleKeyDown} sx={{ '& .MuiOutlinedInput-root': { bgcolor: 'rgba(255,255,255,0.6)', @@ -95,6 +116,7 @@ export default function Login() { placeholder="Enter your password" value={pwd} onChange={(e) => setPwd(e.target.value)} + onKeyDown={handleKeyDown} sx={{ '& .MuiOutlinedInput-root': { bgcolor: 'rgba(255,255,255,0.6)', @@ -119,11 +141,14 @@ export default function Login() { } label={Remember me} /> Forgot password? + + {error && {error}} + + + + + + Assigned Provider (From Survey) + + + setFormData(prev => ({ ...prev, providercompany: val || '', providerlocation: '' }))} + renderInput={(params) => } + /> + + + setFormData(prev => ({ ...prev, providerlocation: val || '' }))} + renderInput={(params) => } + disabled={!formData.providercompany} + /> + + + + Pickup Location + + + + + + + + + + Delivery Destination + + + + + + setFormData(prev => ({ ...prev, deliverycity: val || '' }))} + onInputChange={(e, val) => setFormData(prev => ({ ...prev, deliverycity: val || '' }))} + renderInput={(params) => } + /> + + + + + + + + Parcel Details + + + + {formData.parcels.map((parcel, idx) => ( + + + + + + + + + + + + + + + + + + removeParcel(idx)} disabled={formData.parcels.length === 1}> + + + + + ))} + + + Price & Insurance + + + {quoteInfo && ( + + Estimated Base Price: ₹{quoteInfo.basequote?.toFixed(2)} (Chargeable Weight: {quoteInfo.chargeableweight} kg) + + )} + + + + + + + { + const checked = e.target.checked; + setFormData(prev => ({ + ...prev, + needsinsurance: checked, + insuranceamount: checked && prev.declaredvalue ? (parseFloat(prev.declaredvalue) * 0.01).toFixed(2) : prev.insuranceamount + })); + }} />} + label="Needs Insurance? (1%)" + sx={{ whiteSpace: 'nowrap' }} + /> + { + const val = e.target.value; + setFormData(prev => ({ + ...prev, + declaredvalue: val, + insuranceamount: prev.needsinsurance && val ? (parseFloat(val) * 0.01).toFixed(2) : prev.insuranceamount + })); + }} + /> + + + + + + Additional Information + + + + Normal + Fast Express + + + + + + + + + + + + + + {/* Embedded Client Registration Form */} + {clientDialogOpen && ( + setClientDialogOpen(false)} + onSaved={() => { + setClientDialogOpen(false); + onSave(); // Refetch clients so they appear in dropdown + }} + /> + )} + + ); +} + +export default function Bookings() { + const [bookings, setBookings] = useState([]); + const [clients, setClients] = useState([]); + const [surveys, setSurveys] = useState([]); + const [loading, setLoading] = useState(true); + const [search, setSearch] = useState(''); + const [sourceFilter, setSourceFilter] = useState('All'); + const [formOpen, setFormOpen] = useState(false); + + const loadData = async () => { + setLoading(true); + try { + const [bookingsData, clientsData, surveysData] = await Promise.all([ + apiFetchBookings(), + fetchClients().catch(() => []), + apiFetchSurveys().catch(() => []) + ]); + setBookings(Array.isArray(bookingsData) ? bookingsData : []); + setClients(Array.isArray(clientsData) ? clientsData : []); + setSurveys(Array.isArray(surveysData) ? surveysData : []); + } catch (err) { + console.error(err); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + loadData(); + }, []); + + const displayBookings = bookings.filter(b => { + const matchesSearch = (b.bookingno || '').toLowerCase().includes(search.toLowerCase()) || + (b.pickupaddress || '').toLowerCase().includes(search.toLowerCase()) || + (b.deliverycity || '').toLowerCase().includes(search.toLowerCase()) || + (b.providercompany || '').toLowerCase().includes(search.toLowerCase()); + const matchesSource = sourceFilter === 'All' || b.bookingsource === sourceFilter; + return matchesSearch && matchesSource; + }); + + return ( + + + + + + setSearch(e.target.value)} + size="small" + InputProps={{ + startAdornment: , + sx: { borderRadius: 3, bgcolor: alpha('#e2e8f0', 0.4) } + }} + /> + + + + + setSourceFilter(val)} aria-label="booking source filter tabs"> + + + + + + + {loading ? ( + + ) : ( + + + + + 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'} + + + + + + )) + )} + +
+
+ )} +
+ + setFormOpen(false)} + onSave={() => { loadData(); }} + clients={clients} + surveys={surveys} + /> +
+ ); +} diff --git a/src/pages/pricing/Pricing.jsx b/src/pages/pricing/Pricing.jsx new file mode 100644 index 0000000..6278978 --- /dev/null +++ b/src/pages/pricing/Pricing.jsx @@ -0,0 +1,633 @@ +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, + Dialog, DialogTitle, DialogContent, DialogActions, IconButton, Grid, Tooltip, Alert, Snackbar +} from '@mui/material'; +import AddIcon from '@mui/icons-material/Add'; +import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; +import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline'; +import CloseIcon from '@mui/icons-material/Close'; +import SearchIcon from '@mui/icons-material/Search'; +import StorefrontOutlinedIcon from '@mui/icons-material/StorefrontOutlined'; +import LocalShippingOutlinedIcon from '@mui/icons-material/LocalShippingOutlined'; +import PlaceOutlinedIcon from '@mui/icons-material/PlaceOutlined'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp'; +import PageHeader from '@/components/PageHeader'; +import { fetchUsers } from '@/utils/apiClient'; + +const API_BASE = import.meta.env.VITE_API_BASE || 'https://api.doormile.com/api/v1'; + +const getHeaders = () => { + const token = localStorage.getItem('auth_token'); + return { + 'Content-Type': 'application/json', + ...(token ? { Authorization: `Bearer ${token}` } : {}), + }; +}; + +async function apiFetchPricing() { + const res = await fetch(`${API_BASE}/admin/carrier-pricing?limit=1000`, { headers: getHeaders() }); + if (!res.ok) throw new Error('Failed to fetch pricing'); + const json = await res.json(); + return Array.isArray(json) ? { data: json } : json; +} + +async function apiSavePricing(data) { + const isUpdate = !!data.id; + const url = isUpdate ? `${API_BASE}/admin/carrier-pricing/${data.id}` : `${API_BASE}/admin/carrier-pricing`; + const method = isUpdate ? 'PUT' : 'POST'; + const res = await fetch(url, { + method, + headers: getHeaders(), + body: JSON.stringify({ + company: data.company, + weight_slab: data.weight_slab, + zone: data.zone || '', + service_type: data.service_type || '', + rate: String(data.rate) + }) + }); + if (!res.ok) { + const err = await res.json().catch(() => ({})); + throw new Error(err.error || err.message || 'Failed to save pricing'); + } + return res.status === 204 ? {} : res.json().catch(() => ({})); +} + +const ZONES = ['Local / Same', 'Within Tamilnadu', 'Interstate India']; +const WEIGHT_SLABS = ['< 500g', '500g - 1kg', '1kg - 2kg', '2kg - 5kg', '> 5kg']; + +// isCell=true → editing a specific cell (company/slab/zone locked, only rate editable) +// isCell=false → "Add Rate" button (all fields editable) +function PricingFormDialog({ open, onClose, onSave, initialData, isCell = false, pricingList = [], providerColumns = [], providerUsesServiceType = false }) { + const [formData, setFormData] = useState({}); + const [saving, setSaving] = useState(false); + const [error, setError] = useState(null); + + useEffect(() => { + if (open) { + setFormData(initialData || { company: '', weight_slab: '', zone: '', rate: '' }); + setError(null); + } + }, [open, initialData]); + + const set = (field) => (e) => setFormData(prev => ({ ...prev, [field]: e.target.value })); + + // Column key: zone if available, otherwise service_type (e.g. DTDC) + const colKey = (p) => ((p.zone || '').trim() || (p.service_type || '').trim()); + + const findExistingRecord = (data) => { + const company = (data.company || '').trim(); + const slab = (data.weight_slab || '').trim(); + const col = (data.zone || data.service_type || '').trim(); + return pricingList.find(p => + (p.company || '').trim() === company && + (p.weight_slab || '').trim() === slab && + colKey(p) === col + ) || null; + }; + + const handleSave = async () => { + const colValue = (formData.zone || formData.service_type || '').trim(); + if (!isCell) { + if (!formData.company?.trim()) { setError('Company is required.'); return; } + if (!formData.weight_slab?.trim()) { setError('Weight slab is required.'); return; } + if (!colValue) { setError('Zone / Service Type is required.'); return; } + } + if (!String(formData.rate ?? '').trim()) { setError('Rate is required.'); return; } + + setSaving(true); + setError(null); + try { + let dataToSave = { ...formData }; + // Route the column value to the right field based on provider type + if (!isCell) { + dataToSave = { + ...dataToSave, + zone: providerUsesServiceType ? '' : colValue, + service_type: providerUsesServiceType ? colValue : (dataToSave.service_type || ''), + }; + } + if (!dataToSave.id) { + const existing = findExistingRecord(dataToSave); + if (existing) dataToSave = { ...dataToSave, id: existing.id }; + } + await apiSavePricing(dataToSave); + onSave(); + onClose(); + } catch (err) { + console.error(err); + setError(err.message || 'Failed to save. Please try again.'); + } finally { + setSaving(false); + } + }; + + return ( + <> + setError(null)} + anchorOrigin={{ vertical: 'top', horizontal: 'right' }} + sx={{ zIndex: 9999 }} + > + setError(null)} severity="error" variant="filled" sx={{ width: '100%', boxShadow: 3 }}> + {error} + + + + + + {isCell ? (initialData?.id ? 'Edit Rate' : 'Add Rate') : (initialData?.id ? 'Edit Rate' : 'Add New Rate')} + + + + + {isCell ? ( + + + UPDATING RATE FOR + {formData.company} + {formData.weight_slab} · {formData.zone || formData.service_type} + + + ) : ( + <> + + + + + + {WEIGHT_SLABS.map(s => {s})} + + + + setFormData(prev => ({ + ...prev, + zone: providerUsesServiceType ? '' : e.target.value, + service_type: providerUsesServiceType ? e.target.value : '' + }))} + size="small" + > + {(providerColumns.length > 0 ? providerColumns : ZONES).map(z => ( + {z} + ))} + + + + )} + + + + + + + + + + + + ); +} + +async function apiFetchSurveys() { + const res = await fetch(`${API_BASE}/admin/competitor-branches?limit=1000`, { headers: getHeaders() }); + if (!res.ok) throw new Error('Failed to fetch surveys'); + const json = await res.json(); + const data = Array.isArray(json) ? json : (json.data || []); + return { data, total: data.length }; +} + +export default function Pricing() { + const [pricingList, setPricingList] = useState([]); + const [surveysList, setSurveysList] = useState([]); + const [loading, setLoading] = useState(true); + const [selectedProvider, setSelectedProvider] = useState(''); + const [searchQuery, setSearchQuery] = useState(''); + const [locationsOpen, setLocationsOpen] = useState(false); + const [formOpen, setFormOpen] = useState(false); + const [editingRecord, setEditingRecord] = useState(null); + const [isEditMode, setIsEditMode] = useState(false); + const [isCellEdit, setIsCellEdit] = useState(false); + const [users, setUsers] = useState([]); + + const loadData = () => { + let cancelled = false; + setLoading(true); + Promise.all([ + apiFetchPricing().catch(() => ({ data: [] })), + apiFetchSurveys().catch(() => ({ data: [] })), + fetchUsers().catch(() => []) + ]).then(([pRes, sRes, userRes]) => { + if (!cancelled) { + setPricingList(Array.isArray(pRes) ? pRes : (pRes.data || [])); + setSurveysList(sRes.data || sRes || []); + setUsers(Array.isArray(userRes) ? userRes : (userRes.data || [])); + } + }).catch(err => console.error("Failed to fetch data", err)) + .finally(() => { + if (!cancelled) setLoading(false); + }); + return () => { cancelled = true; }; + }; + + useEffect(() => { + return loadData(); + }, []); + + // Ensure absolutely unique companies and default selection + const companies = [...new Set(pricingList.map(p => p.company).filter(Boolean))]; + useEffect(() => { + if (!selectedProvider && companies.length > 0) { + setSelectedProvider(companies[0]); + } + }, [companies, selectedProvider]); + + const providerRates = pricingList.filter(p => p.company === selectedProvider); + // Normalize: lowercase, trim edges, collapse internal spaces + const normCompany = (s) => (s || '').toLowerCase().trim().replace(/\s+/g, ' '); + const normProvider = normCompany(selectedProvider); + const providerSurveys = surveysList.filter(p => { + const pn = normCompany(p.company); + // Exact after normalising, or one name starts with the other (handles "Pvt Ltd" suffixes) + return pn === normProvider || pn.startsWith(normProvider) || normProvider.startsWith(pn); + }); + + // Sort by ID desc so most recent record wins when duplicates exist + const sortedProviderRates = [...providerRates].sort((a, b) => (b.id || 0) - (a.id || 0)); + + // Extract numeric weight from a slab string for sorting rows logically + const slabSortWeight = (slab) => { + const s = (slab || '').toLowerCase().replace(/\s+/g, ''); + const nums = s.match(/[\d.]+/g); + if (!nums) return 9999; + const first = parseFloat(nums[0]); + const grams = s.includes('kg') ? first * 1000 : first; + if (s.startsWith('<') || s.startsWith('upto')) return grams - 0.5; + if (s.startsWith('>') || s.includes('above') || s.endsWith('+')) return grams + 0.5; + return grams; + }; + + // Use zone if available, fall back to service_type (e.g. DTDC has service_type instead of zone) + const getColKey = (r) => ((r.zone || '').trim() || (r.service_type || '').trim()); + const providerUsesServiceType = sortedProviderRates.length > 0 && + sortedProviderRates.every(r => !(r.zone || '').trim()); + + // Dynamic columns — whichever field the provider uses for differentiation + const providerZones = [...new Set(sortedProviderRates.map(getColKey).filter(Boolean))].sort(); + + // Dynamic rows: every unique weight slab, sorted by actual weight + const allProviderSlabs = [...new Set(sortedProviderRates.map(r => (r.weight_slab || '').trim()).filter(Boolean))] + .sort((a, b) => slabSortWeight(a) - slabSortWeight(b)); + + const displaySlabs = searchQuery + ? allProviderSlabs.filter(s => s.toLowerCase().includes(searchQuery.toLowerCase())) + : allProviderSlabs; + + return ( + + + + + + {/* Modern Premium Control Panel */} + + + + + + + + Active Logistics Provider + + + + + + + + + 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' } } + } + }} + /> + + + + + + {/* 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 */} + + + + + + + + + + + + + + + + ALL SERVICED AREAS: + + + {providerSurveys.map((s, idx) => ( + + ))} + + + + + )} + + {/* Pricing Data Grid */} + + {loading ? ( + + + + ) : ( + + + + + Weight Slab + {providerZones.map(zone => ( + + {zone} + + ))} + + + + {providerZones.length === 0 ? ( + + + + No pricing data for this provider yet. + + + + ) : displaySlabs.length === 0 ? ( + + + + No weight slabs match your search. + + + + ) : ( + displaySlabs.map((slab) => ( + + + {slab} + + {providerZones.map(zone => { + // Exact match — use getColKey so DTDC service_type columns work too + const match = sortedProviderRates.find(r => (r.weight_slab || '').trim() === slab && getColKey(r) === zone); + const hasRate = !!match; + const rateStr = match ? `₹${match.rate}` : '—'; + + let tooltipText = ''; + if (match && match.updated_by) { + const updater = users.find(u => u.id === match.updated_by); + tooltipText = `Last updated by: ${updater ? updater.first_name : 'ID ' + match.updated_by}`; + } + + const openCellEdit = () => { + setEditingRecord({ + id: match ? match.id : undefined, + company: selectedProvider, + weight_slab: slab, + zone: providerUsesServiceType ? '' : zone, + service_type: providerUsesServiceType ? zone : (match?.service_type || ''), + rate: match ? String(match.rate) : '' + }); + setIsCellEdit(true); + setFormOpen(true); + }; + + return ( + + + + + {rateStr} + + {isEditMode && ( + hasRate + ? + : + )} + + + + ); + })} + + )) + )} + +
+
+ )} +
+
+ + setFormOpen(false)} + onSave={() => loadData()} + initialData={editingRecord} + isCell={isCellEdit} + pricingList={pricingList} + providerColumns={providerZones} + providerUsesServiceType={providerUsesServiceType} + /> +
+ ); +} diff --git a/src/pages/survey/Survey.jsx b/src/pages/survey/Survey.jsx new file mode 100644 index 0000000..8245f49 --- /dev/null +++ b/src/pages/survey/Survey.jsx @@ -0,0 +1,895 @@ +import { useState, useEffect, Fragment } from 'react'; +import { fetchUsers, deleteCompetitorBranch } from '@/utils/apiClient'; +import { + Card, Box, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, + Typography, TextField, InputAdornment, IconButton, Collapse, TablePagination, Stack, CircularProgress, alpha, + Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, MenuItem, Alert, Autocomplete, Snackbar +} from '@mui/material'; +import SearchIcon from '@mui/icons-material/Search'; +import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; +import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; +import PhoneOutlinedIcon from '@mui/icons-material/PhoneOutlined'; +import PlaceOutlinedIcon from '@mui/icons-material/PlaceOutlined'; +import AssignmentOutlinedIcon from '@mui/icons-material/AssignmentOutlined'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp'; +import StorefrontOutlinedIcon from '@mui/icons-material/StorefrontOutlined'; +import LocalShippingOutlinedIcon from '@mui/icons-material/LocalShippingOutlined'; +import MapOutlinedIcon from '@mui/icons-material/MapOutlined'; +import { Avatar, Button, Chip, Grid } from '@mui/material'; +import AddIcon from '@mui/icons-material/Add'; +import CloseIcon from '@mui/icons-material/Close'; + +import PageHeader from '@/components/PageHeader'; +import StatCard from '@/components/StatCard'; + +const API_BASE = import.meta.env.VITE_API_BASE || 'https://api.doormile.com/api/v1'; + +const getHeaders = () => { + const token = localStorage.getItem('auth_token'); + return { + 'Content-Type': 'application/json', + ...(token ? { 'Authorization': `Bearer ${token}` } : {}) + }; +}; + +const FIELD_LABEL_SX = { textTransform: 'uppercase', letterSpacing: 0.4, fontSize: '0.68rem', fontWeight: 700, color: 'grey.700' }; + +function Pill({ label, color = 'default' }) { + if (!label) return ; + return ( + + ); +} + +function Field({ label, children }) { + return ( + + {label} + {children} + + ); +} + +function MagicStatCard({ title, value, icon: Icon, gradient, percentage }) { + return ( + + + + + {title} + + + + + + + + {value} + + {percentage && ( + + )} + + + + + ); +} + +function SectionCard({ icon: Icon, title, children }) { + return ( + + + + {title} + + {children} + + ); +} + +async function apiFetchSurveys() { + const res = await fetch(`${API_BASE}/admin/competitor-branches?limit=1000`, { headers: getHeaders() }); + if (!res.ok) throw new Error('Failed to fetch surveys'); + const json = await res.json(); + const data = Array.isArray(json) ? json : (json.data || []); + return { data, total: data.length }; +} + +async function apiSaveSurvey(data) { + const isUpdate = !!data.id; + const url = isUpdate ? `${API_BASE}/admin/competitor-branches/${data.id}` : `${API_BASE}/admin/competitor-branches`; + const method = isUpdate ? 'PUT' : 'POST'; + const res = await fetch(url, { + method, + headers: getHeaders(), + body: JSON.stringify(data) + }); + if (!res.ok) { + const err = await res.json().catch(() => ({})); + throw new Error(err.error || err.message || 'Failed to save survey'); + } + return res.status === 204 ? {} : res.json().catch(() => ({})); +} + +async function apiSavePricing(data) { + const isUpdate = !!data.id; + const url = isUpdate ? `${API_BASE}/admin/carrier-pricing/${data.id}` : `${API_BASE}/admin/carrier-pricing`; + const method = isUpdate ? 'PUT' : 'POST'; + const res = await fetch(url, { + method, + headers: getHeaders(), + body: JSON.stringify({ + company: data.company, + weight_slab: data.weight_slab, + zone: data.zone || '', + service_type: data.service_type || '', + rate: String(data.rate) + }) + }); + if (!res.ok) { + const err = await res.json().catch(() => ({})); + throw new Error(err.error || err.message || 'Failed to save pricing'); + } + return res.status === 204 ? {} : res.json().catch(() => ({})); +} + +const ZONES = ['Local / Same', 'Within Tamilnadu', 'Interstate India']; +const WEIGHT_SLABS = ['< 500g', '500g - 1kg', '1kg - 2kg', '2kg - 5kg', '> 5kg']; + +function SurveyFormDialog({ open, onClose, onSave, initialData }) { + const [formData, setFormData] = useState({}); + const [saving, setSaving] = useState(false); + const [error, setError] = useState(null); + + useEffect(() => { + if (open) { + setFormData(initialData ? { + ...initialData, + pincodes: initialData.pincodes ? initialData.pincodes.split(',').map(s => s.trim()).filter(Boolean) : [] + } : { + company: '', area: '', phone: '', rate_per_kg: '', + offers_pickup: 'no', offers_drop: 'no', packing_charge: '', + time_in_days: '', plus_code: '', address: '', frequency: '', + pincodes: [], + slabs: [] + }); + setError(null); + } + }, [open, initialData]); + + const handleChange = (field) => (e) => { + setFormData(prev => ({ ...prev, [field]: e.target.value })); + }; + + const handleSlabChange = (index, field) => (e) => { + const newSlabs = [...(formData.slabs || [])]; + newSlabs[index] = { ...newSlabs[index], [field]: e.target.value }; + setFormData(prev => ({ ...prev, slabs: newSlabs })); + }; + + const addSlab = () => { + setFormData(prev => ({ + ...prev, + slabs: [...(prev.slabs || []), { weight_slab: '', zone: '', service_type: '', rate: '' }] + })); + }; + + const removeSlab = (index) => { + const newSlabs = [...(formData.slabs || [])]; + newSlabs.splice(index, 1); + setFormData(prev => ({ ...prev, slabs: newSlabs })); + }; + + const handlePincodeChange = (index) => (e) => { + const newPincodes = [...(formData.pincodes || [])]; + newPincodes[index] = e.target.value; + setFormData(prev => ({ ...prev, pincodes: newPincodes })); + }; + + const addPincode = () => { + setFormData(prev => ({ + ...prev, + pincodes: [...(prev.pincodes || []), ''] + })); + }; + + const removePincode = (index) => { + const newPincodes = [...(formData.pincodes || [])]; + newPincodes.splice(index, 1); + setFormData(prev => ({ ...prev, pincodes: newPincodes })); + }; + + const handleSave = async () => { + setSaving(true); + setError(null); + try { + if (formData.pincodes && formData.pincodes.length > 0) { + const invalidPincodes = formData.pincodes.filter(p => p.trim() && !/^\d{6}$/.test(p.trim())); + if (invalidPincodes.length > 0) { + throw new Error(`Invalid pincodes detected: ${invalidPincodes.join(', ')}. All pincodes must be exactly 6 numeric digits.`); + } + } + + const payload = { + ...(formData.id ? { id: formData.id } : {}), + company: formData.company || '', + area: formData.area || '', + phone: formData.phone || '', + rate_per_kg: formData.rate_per_kg || '', + offers_pickup: formData.offers_pickup || 'no', + offers_drop: formData.offers_drop || 'no', + packing_charge: formData.packing_charge || '', + time_in_days: formData.time_in_days || '', + plus_code: formData.plus_code || '', + address: formData.address || '', + frequency: formData.frequency || '', + pincodes: formData.pincodes ? formData.pincodes.join(',') : '', + }; + await apiSaveSurvey(payload); + + if (formData.slabs && formData.slabs.length > 0 && formData.company) { + try { + await Promise.all(formData.slabs.map(slab => { + if (slab.weight_slab && slab.rate) { + return apiSavePricing({ + company: formData.company, + weight_slab: slab.weight_slab, + zone: slab.zone || '', + service_type: slab.service_type || '', + rate: slab.rate + }); + } + return Promise.resolve(); + })); + } catch (err) { + console.error("Failed to save pricing slabs", err); + // Continue anyway since survey was saved + } + } + + onSave(); + onClose(); + } catch (err) { + console.error(err); + setError(err.message || 'Failed to save. Please try again.'); + } finally { + setSaving(false); + } + }; + + return ( + <> + setError(null)} + anchorOrigin={{ vertical: 'top', horizontal: 'right' }} + sx={{ zIndex: 9999 }} + > + setError(null)} severity="error" variant="filled" sx={{ width: '100%', boxShadow: 3 }}> + {error} + + + + + {initialData ? 'Edit Survey Record' : 'Add New Survey'} + + + + + + + + + + + + + + + + + Yes + No + + + + + Yes + No + + + + + + + + + + + Daily + Weekly + Bi-weekly + Monthly + On-demand + + + + + + + + + SERVICEABLE PINCODES + + + {(formData.pincodes || []).map((pincode, index) => ( + + + + + + removePincode(index)}> + + + + + ))} + {(!formData.pincodes || formData.pincodes.length === 0) && ( + + Click 'Add Pincode' to assign locations to this competitor. + + )} + + + + + + + + + + PRICING SLABS (OPTIONAL) + + + {(formData.slabs || []).map((slab, index) => ( + + + + {WEIGHT_SLABS.map(s => {s})} + + + + + {ZONES.map(z => {z})} + + + + + + + + + + removeSlab(index)}> + + + + + ))} + {(!formData.slabs || formData.slabs.length === 0) && ( + + Click 'Add Slab' to add pricing entries for this competitor. + + )} + + + + + + + + + + + ); +} + +function BranchCard({ row, onEdit, onDelete, users }) { + const [open, setOpen] = useState(false); + const creator = users.find(u => u.id === row.created_by); + const updater = users.find(u => u.id === row.updated_by); + + return ( + + setOpen(!open)} + sx={{ cursor: 'pointer', userSelect: 'none' }} + > + + {/* Left: Location & Contact */} + + + + + + + {row.area || 'Unknown Area'} + + + {row.phone || '—'} + + + + + {/* Middle: Rates */} + + + RATE PER KG + + + + LOGISTICS + + {row.offers_pickup === 'yes' || row.offers_drop === 'yes' ? `${row.offers_pickup === 'yes' ? 'Pickup' : ''} ${row.offers_drop === 'yes' ? 'Drop' : ''}` : '—'} + + + + + {/* Right: Actions */} + + + { e.stopPropagation(); onEdit(row); }} sx={{ color: 'grey.400', '&:hover': { color: '#c01227', bgcolor: alpha('#c01227', 0.1) } }}> + + + { e.stopPropagation(); onDelete(row); }} sx={{ color: 'grey.400', '&:hover': { color: '#ef4444', bgcolor: alpha('#ef4444', 0.1) } }}> + + + + + + + + + + + + + + + + {row.offers_pickup || '—'} + + + {row.offers_drop || '—'} + + + {row.packing_charge || '—'} + + + + + + + + {row.time_in_days || '—'} + + + {row.company || '—'} + + + {row.frequency || '—'} + + + {row.phone || '—'} + + + + + + + + {row.area || '—'} + + + {row.pincodes ? ( + + {row.pincodes.split(',').map((p, i) => ( + + ))} + + ) : ( + + )} + + + {row.plus_code || '—'} + + + + + + {row.address || '—'} + + {(row.address || row.plus_code) && ( + + )} + + + + + + + + + + + + {creator ? creator.first_name : (row.created_by ? `User ID: ${row.created_by}` : 'System')} + + + + + + + {updater ? updater.first_name : (row.updated_by ? `User ID: ${row.updated_by}` : '—')} + + + + + + + + + + + ); +} + +function CompanyGroup({ companyName, branches, onEdit, onDelete, users }) { + const [open, setOpen] = useState(false); + + return ( + + setOpen(!open)} + sx={{ cursor: 'pointer', userSelect: 'none' }} + > + + {/* Left Side: Avatar & Basic Info */} + + + {(companyName || 'A')[0].toUpperCase()} + + + + {companyName || 'Unknown Company'} + + + {branches.length} Location{branches.length !== 1 && 's'} Surveyed + + + + + {/* Right Side: Badges & Actions */} + + + + + + + + + + + + + + + + {branches.map((branch, idx) => )} + + + + + ); +} + +export default function Survey() { + const [data, setData] = useState([]); + const [loading, setLoading] = useState(true); + const [search, setSearch] = useState(''); + const [page, setPage] = useState(0); + const [rowsPerPage, setRowsPerPage] = useState(5); + const [formOpen, setFormOpen] = useState(false); + const [editingRecord, setEditingRecord] = useState(null); + const [toDelete, setToDelete] = useState(null); + const [deleting, setDeleting] = useState(false); + const [deleteError, setDeleteError] = useState(null); + + const [users, setUsers] = useState([]); + + const loadData = () => { + let cancelled = false; + setLoading(true); + Promise.all([ + apiFetchSurveys(), + fetchUsers().catch(() => []) + ]) + .then(([res, userRes]) => { + if (!cancelled) { + setData(res.data || res || []); + setUsers(userRes || []); + } + }) + .catch(err => console.error("Error fetching data:", err)) + .finally(() => { + if (!cancelled) setLoading(false); + }); + return () => { cancelled = true; }; + }; + + useEffect(() => { + return loadData(); + }, []); + + const confirmDelete = async () => { + setDeleting(true); + setDeleteError(null); + try { + await deleteCompetitorBranch(toDelete.id); + setToDelete(null); + loadData(); + } catch (e) { + setDeleteError(e.message || 'Failed to delete. Please try again.'); + } finally { + setDeleting(false); + } + }; + + const filtered = data.filter(r => + (r.company || '').toLowerCase().includes(search.toLowerCase()) || + (r.area || '').toLowerCase().includes(search.toLowerCase()) || + (r.phone || '').toLowerCase().includes(search.toLowerCase()) + ); + + // Group filtered records by company + const groupedData = filtered.reduce((acc, row) => { + const comp = row.company || 'Unknown Company'; + if (!acc[comp]) acc[comp] = []; + acc[comp].push(row); + return acc; + }, {}); + + const companyKeys = Object.keys(groupedData).sort(); + const paginatedKeys = companyKeys.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage); + + const stats = { + total: data.length, + quoted: data.filter(d => d.rate_per_kg && !String(d.rate_per_kg).toLowerCase().includes('not answered')).length, + missing: data.filter(d => !d.rate_per_kg || String(d.rate_per_kg).toLowerCase().includes('not answered')).length, + }; + + return ( + + + + + + + + + + + + + + + + + {/* Header Section */} + + + + + + + Survey Records + + + Manage business enquiry and survey data from the field + + + + + {/* Toolbar Section */} + + { setSearch(e.target.value); setPage(0); }} + sx={{ width: { xs: '100%', md: 400 } }} + InputProps={{ + startAdornment: , + }} + /> + + {!loading && ( + + {filtered.length} records + + )} + + + + + {/* List Section */} + + {loading ? ( + + + + ) : paginatedKeys.length > 0 ? ( + + {paginatedKeys.map((companyName) => ( + { setEditingRecord(row); setFormOpen(true); }} + onDelete={(row) => setToDelete(row)} + users={users} + /> + ))} + + ) : ( + + + No survey records found. + Try adjusting your search filters. + + )} + + + {/* Pagination Section */} + + setPage(newPage)} + rowsPerPage={rowsPerPage} + onRowsPerPageChange={(e) => { setRowsPerPage(parseInt(e.target.value, 10)); setPage(0); }} + rowsPerPageOptions={[5, 10, 25]} + labelRowsPerPage="Companies per page:" + /> + + + + setFormOpen(false)} + onSave={() => loadData()} + initialData={editingRecord} + /> + + { setToDelete(null); setDeleteError(null); }}> + Delete survey record? + + + This will permanently remove the record for {toDelete?.area || toDelete?.company}. This cannot be undone. + + {deleteError && {deleteError}} + + + + + + + + ); +} diff --git a/src/pages/survey/data.json b/src/pages/survey/data.json new file mode 100644 index 0000000..0a556ad --- /dev/null +++ b/src/pages/survey/data.json @@ -0,0 +1 @@ +[{"id": 1, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "DTDC", "area": "Townhall", "phone": "4433004444", "plusCode": "2X24+6Q Coimbatore, Tamil Nadu", "address": "Ground Floor, Subramani Building, 194, Rangai Gowder St, near Bank Of Maharashtra, Sukrawarpet, Sukrawar Pettai, Town Hall, Coimbatore, Tamil Nadu 641001"}, {"id": 2, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": null, "plusCode": "XXV4+WW Coimbatore, Tamil Nadu", "address": "685, Sri complex, Raja St, opp. to post office, Town Hall, Coimbatore, Tamil Nadu 641001"}, {"id": 3, "rate": "Not answered", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "DB Road", "phone": "9442740301", "plusCode": "2W7X+XQ Coimbatore, Tamil Nadu", "address": "594 Obli Towers DB Road, D.B Road, Coimbatore, Tamil Nadu 641002"}, {"id": 4, "rate": "Not answered", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Gopalapuram", "phone": "4224954630", "plusCode": "2X29+X3 Coimbatore, Tamil Nadu", "address": "789, AR Complex, Avinashi Rd, Near Dhandumariamman Kovil, Gopalapuram, Coimbatore, Tamil Nadu 641018"}, {"id": 5, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Gandhipuram", "phone": "9363344055", "plusCode": null, "address": "XX2G+W4V, Pollachi Main Rd, Gandhipuram, Gandhi Nagar, Gandhipuram, Kurichi, Tamil Nadu 641024"}, {"id": 6, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": null, "plusCode": "2XC9+3G Gandhipuram, Tamil Nadu", "address": "42B, 2nd Floor, Rajeshwari Complex, Dr Rajendra Prasad Rd, Cross Cut, Gandhipuram, Coimbatore, Tamil Nadu 641012"}, {"id": 7, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "7695858583", "plusCode": "2X97+22 Gandhipuram, Tamil Nadu", "address": "Arba Tower, 885, 11th St, Gandhipuram, Coimbatore, Tamil Nadu 641012"}, {"id": 8, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Papanaickenpalayam", "phone": "9843539643", "plusCode": "2X8M+3J Pappanaickenpalayam, Tamil Nadu", "address": "Shop No. 90, New Scheme Rd, Pappanaickenpalayam, Coimbatore, Tamil Nadu 641037"}, {"id": 9, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9543430401", "plusCode": "5W3V+GQ Periyanaickenpalayam, Tamil Nadu", "address": "No.295/1A1A2,Periyanaickenpalayam near Nathan Hall, Mettupalayam Rd, near Poorvika Mobiles, Periyanaickenpalayam, Tamil Nadu 641020"}, {"id": 10, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": null, "plusCode": null, "address": "Plot No 92, Parameshwaran Layout Rd, behind Lakshmi Mill Quarters, Papanaickenpalayam, Coimbatore, Tamil Nadu 641037"}, {"id": 11, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "RS Puram", "phone": "8807080576", "plusCode": null, "address": "81, Light House Rd, Kollupuram, R.S. Puram, Coimbatore, Tamil Nadu 641001"}, {"id": 12, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9894513161", "plusCode": "2W6X+3X Coimbatore, Tamil Nadu", "address": "196/4, Thiruvenkatasamy Rd W, near ICICI BANK, R.S. Puram, Coimbatore, Tamil Nadu 641002"}, {"id": 13, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9600794923", "plusCode": "2W5V+2X Coimbatore, Tamil Nadu", "address": "Shop No, pal company, bus stop, 1283, Thadagam Main Rd, Near Milk Company, R.S. Puram, Coimbatore, Tamil Nadu 641002"}, {"id": 14, "rate": "Switch off", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Race Cource", "phone": "9843198433", "plusCode": "2X3G+XP Coimbatore, Tamil Nadu", "address": "9, GD Naidu St, Race Course, Coimbatore, Tamil Nadu 641018"}, {"id": 15, "rate": 90.0, "pickup": "yes", "drop": "yes", "timeInDays": null, "packing": "no", "company": null, "area": "Ram Nagar", "phone": "9952165755", "plusCode": "2X57+MW Coimbatore, Tamil Nadu", "address": "Shop No.100/34, Ranga Konar St, Kattoor Main, Ram Nagar, Coimbatore, Tamil Nadu 641009"}, {"id": 16, "rate": "Not answered", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Venkitta Puram", "phone": "9994997374", "plusCode": "2WCQ+G2 Coimbatore, Tamil Nadu", "address": "193/9, GCT POST,, Thadagam Main Road, GCT Post, Venkitta Puram, Coimbatore, Tamil Nadu 641011"}, {"id": 17, "rate": 120.0, "pickup": "yes", "drop": "yes", "timeInDays": "3 - 5", "packing": 10.0, "company": null, "area": "Ramanathapuram", "phone": "9965654003", "plusCode": null, "address": "XXVX+537, Nanjundapuram Rd, Ramanathapuram, Coimbatore, Tamil Nadu 641045"}, {"id": 18, "rate": "Not answered", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "7200381907", "plusCode": "X2W3+VP Ramanathapuram, Tamil Nadu", "address": "1/1A, Trichy Rd, near Alverniya Mat. Hr. Sec. School, Kothari Layout, Ramanathapuram, Coimbatore, Tamil Nadu 641045"}, {"id": 19, "rate": "Not answered", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9843767600", "plusCode": "XXXW+73 Ramanathapuram, Tamil Nadu", "address": "122, Pankaja Mills Rd, Ramasamy Nagar, Puliakulam, Ramanathapuram, Coimbatore, Tamil Nadu 641045"}, {"id": 20, "rate": 120.0, "pickup": "yes", "drop": "yes", "timeInDays": "3 - 4", "packing": 150.0, "company": null, "area": "Gandhi Park", "phone": "8608866423", "plusCode": "2X22+35 Coimbatore, Tamil Nadu", "address": "124, Sundaram St, Gandhi Park, Cricle, Coimbatore, Tamil Nadu 641001"}, {"id": 21, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Siddhapudur", "phone": "9843479246", "plusCode": "2X9H+P8 New Siddhapudur, Tamil Nadu", "address": "Shop No, 291, Avaram Palayam Rd, Siddhapudur, Illango Nagar, New Siddhapudur, Coimbatore, Tamil Nadu 641044"}, {"id": 22, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Saibaba Colony", "phone": "9944441534", "plusCode": "2WGR+6G Coimbatore, Tamil Nadu", "address": "Pasumai Pharmacy Backside, 194A, NSR Rd, Saibaba Colony, Coimbatore, Tamil Nadu 641011"}, {"id": 23, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "NAVA INDIA", "phone": "9597050575", "plusCode": "2X8Q+CR Coimbatore, Tamil Nadu", "address": "NAVA INDIA, NO 498, Avinashi Rd, opp. to KFC, Peelamedu, Pudur, Coimbatore, Tamil Nadu 641004"}, {"id": 24, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Trichy Road", "phone": "9600574650", "plusCode": "XXXV+96 Coimbatore, Tamil Nadu", "address": "17/1699c, jayasree complex, tirchy road, Coimbatore, Tamil Nadu 641045"}, {"id": 25, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Ganapathy", "phone": "7305770577", "plusCode": "2XHG+X5 Coimbatore, Tamil Nadu", "address": "484,2nd Floor, Vijay Complex, Textool Stop, Sathy Rd, above Ruby School, Ganapathy, Coimbatore, Tamil Nadu 641006"}, {"id": 26, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9842383115", "plusCode": "2XPJ+83 Coimbatore, Tamil Nadu", "address": "Kavitha ,sree sastha bavanam 32, p&t colony, Ganapathy Housing Unit, Coimbatore, Tamil Nadu 641006"}, {"id": 27, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "4222537454", "plusCode": "2XWP+GR Coimbatore, Tamil Nadu", "address": "256, sakthi road, ganapathybus stand opp, ganapathy, Coimbatore, Tamil Nadu 641006"}, {"id": 28, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": null, "plusCode": "2XQW+X5 Coimbatore, Tamil Nadu", "address": "1,12C4-3, : 12C4, FCI Rd, Yogi Ram Nagar, VG Rao Nagar, Ganapathy, Coimbatore, Tamil Nadu 641006"}, {"id": 29, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9443559030", "plusCode": "2XJG+78 Coimbatore, Tamil Nadu", "address": "430, 2, Sathy Rd, Sivasakthi Colony, Ganapathy, Coimbatore, Tamil Nadu 641006"}, {"id": 30, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "8088843354", "plusCode": "2XVP+76 Coimbatore, Tamil Nadu", "address": "Shop No 3, near Shiva Hospital, Gem Colony, Ganapathy, Coimbatore, Tamil Nadu 641006"}, {"id": 31, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Selvapuram", "phone": "9944660442", "plusCode": "XWQQ+53 Selvapuram, Tamil Nadu", "address": "366-D, Lakshmi Building, Perur Main Rd, Durai Illam, Selvapuram South, Selvapuram, Coimbatore, Tamil Nadu 641026"}, {"id": 32, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Peelamedu", "phone": "8870870150", "plusCode": "2XCX+VP Coimbatore, Tamil Nadu", "address": "12A, Jaganathan Colony, Avinashi Rd, opp. Pillar 182, Peelamedu, Coimbatore, Tamil Nadu 641004"}, {"id": 33, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "8825977653", "plusCode": "22G9+FJ Coimbatore, Tamil Nadu", "address": "RR COMPLEX, 1597, Avinashi Rd, opp. PILLAR 108, CBE, Hope College, PEELAMEDU, Coimbatore, Tamil Nadu 641004"}, {"id": 34, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "4224397760", "plusCode": "2X54+R4 Coimbatore, Tamil Nadu", "address": "1421, P.S.G. College Stop, Near Krishnamal College, Peelamedu, Coimbatore, Tamil Nadu 641002"}, {"id": 35, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Lawley Road", "phone": "9894436204", "plusCode": "2W7R+7F Coimbatore, Tamil Nadu", "address": "Sakthi Mariyamman Temple, 90-93, Lawley Rd, Coimbatore, Tamil Nadu 641003"}, {"id": 36, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Nallapalayam", "phone": "9944251301", "plusCode": null, "address": "2XW2+HW6, Ramasamy Nagar Extension I, Nallampalayam, Coimbatore, Tamil Nadu 641006"}, {"id": 37, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "PN Pudur", "phone": "9894617355", "plusCode": "2WC9+3G P N Pudur, Tamil Nadu", "address": "Marudhamalai Rd, Veerasamy Colony, P N Pudur, Coimbatore, Tamil Nadu 641041"}, {"id": 38, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Kuniyamuthur", "phone": "8870806398", "plusCode": "XX33+RG Coimbatore, Tamil Nadu", "address": "Palakkad - Coimbatore Rd, above Indian bakery, near sathya showroom Vidhyas textile, Rathinapuri, Thirunavukarsu Nagar, Kuniyamuthur, Coimbatore, Tamil Nadu 641008"}, {"id": 39, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9600480743", "plusCode": "XX63+MV Coimbatore, Tamil Nadu", "address": "8/168A, Pal Pannai St, nearby New Steel House, opp. to Poorvika Mobiles cut, Pulakadu, Kuniyamuthur, Coimbatore, Tamil Nadu 641008"}, {"id": 40, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Podanur", "phone": "9894087481", "plusCode": "XX7P+7M Podanur, Tamil Nadu", "address": "134, Church Rd, Podanur, Coimbatore, Tamil Nadu 641023"}, {"id": 41, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Avarampalayam", "phone": null, "plusCode": null, "address": "2XHP+W6V, Bannariamman Street, Avarampalayam, Illango Nagar, Coimbatore, Tamil Nadu 641006"}, {"id": 42, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Vadavalli", "phone": "4224275041", "plusCode": "2WF4+RC Coimbatore, Tamil Nadu", "address": "A-1, Below Honda Showroom, 5C, Thondamuthur Main Road, Vadavalli, Coimbatore, Tamil Nadu 641041"}, {"id": 43, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Sowripalayam", "phone": "9003573963", "plusCode": "2264+3W Sowri Palayam, Tamil Nadu", "address": "Shop No. 103, Car St, Sowri Palayam, Coimbatore, Tamil Nadu 641028"}, {"id": 44, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Velloalore", "phone": "9944354232", "plusCode": "X282+9W Coimbatore, Tamil Nadu", "address": "15/3-1, Vellalur Rd,Mahalingapuram, vellalore, Coimbatore, Tamil Nadu 64111, Coimbatore, Tamil Nadu 641111"}, {"id": 45, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "KNG Pudur", "phone": null, "plusCode": "3W29+M7 Coimbatore, Tamil Nadu", "address": "Thadagam Main Rd, KNG Pudur Pirivu, Coimbatore, Tamil Nadu 641025"}, {"id": 46, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Tatabad", "phone": null, "plusCode": "3W5W+J5 Coimbatore, Tamil Nadu", "address": "G2, MAYURA COMPLEX, METTUPALAYAM ROAD Sreevatsa Enclave, G.N. MILL, Tatabad, Coimbatore, Tamil Nadu 641029"}, {"id": 47, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "VK Road", "phone": "8675117373", "plusCode": "22P7+WX Coimbatore, Tamil Nadu", "address": "A1 MAHALAKSHMI COMPLEX,THANEERPANTHAL V.K.ROAD,NEAR MANCHESTER SITARA APARTMENT, Coimbatore, Tamil Nadu 641004"}, {"id": 48, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Vedapatti", "phone": "7418764344", "plusCode": "2V2V+GH Coimbatore, Tamil Nadu", "address": "Vedapatti Rd, Vedapatti, Coimbatore, Tamil Nadu 641007"}, {"id": 49, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Pachapalayam", "phone": "9500403329", "plusCode": "XW92+57 Coimbatore, Tamil Nadu", "address": "Ruby Nagar, Siruvani Main Rd, Awin Nagar 1st, Pachapalayam, Coimbatore, Tamil Nadu 641010"}, {"id": 50, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Vellakinar", "phone": null, "plusCode": "3XG5+28 Coimbatore, Tamil Nadu", "address": "RTO Test ground, Near Thudiyalur, Thudiyalur - Saravanampatti Rd, Vellakinar, Coimbatore, Tamil Nadu 641029"}, {"id": 51, "rate": 120.0, "pickup": "yes", "drop": "yes", "timeInDays": "2 -3", "packing": "> 50", "company": null, "area": "Ondipudur", "phone": "9994432955", "plusCode": "223X+CH Ondipudur, Tamil Nadu", "address": "177, Trichy Rd, near nellai kannan sweets shop, Ellammal Layout, Ondipudur, Coimbatore, Tamil Nadu 641016"}, {"id": 52, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Irugur", "phone": "9361101155", "plusCode": "2388+J5 Coimbatore, Tamil Nadu", "address": "SF No.: 302, 7E, Ginning Factory Rd, Coimbatore, Irugur, Tamil Nadu 641103"}, {"id": 53, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Pattanamputhur", "phone": "9597552489", "plusCode": "X3Q5+PC Coimbatore, Tamil Nadu", "address": "Pattanam Rd, near gee studio, near ration shop, Gajalakshmi Nagar, Pattanam Pudur, Coimbatore, Tamil Nadu 641016"}, {"id": 54, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Idigarai", "phone": "9626133193", "plusCode": "4X86+JR Coimbatore, Tamil Nadu", "address": "18, THUDIYALUR MAIN ROAD, OPP TO RICE MILL, IDIGARAI, Coimbatore, Tamil Nadu 641022"}, {"id": 55, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Kannampalayam", "phone": null, "plusCode": null, "address": "235F+5VF, Pirivu, Chinthamanipudur, Kannampalayam, Tamil Nadu 641103"}, {"id": 56, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9361101155", "plusCode": "236J+49 Coimbatore, Tamil Nadu", "address": "Sivasakthi complex, pappampatti perivu, Selvaraja Puram, N.K. Palayam, Coimbatore, Kannampalayam, Tamil Nadu 641103"}, {"id": 57, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Villankurichi", "phone": null, "plusCode": null, "address": "3248+2RR, Cheran ma Nagar, Villankurichi, Coimbatore, Tamil Nadu 641051"}, {"id": 58, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Kurumbapalayam", "phone": "9842244515", "plusCode": "426J+W2 Kurumbapalayam SSKulam, Tamil Nadu", "address": "Adithya Institute of Technology Rd, EB Colony, Kurumbapalayam SSKulam, Tamil Nadu 641107"}, {"id": 59, "rate": "80 - 110", "pickup": "yes", "drop": "yes", "timeInDays": "2 - 4", "packing": 20.0, "company": null, "area": "Chettipalayam", "phone": "9944354232", "plusCode": "W28Q+WC Chettipalayam, Tamil Nadu", "address": "Shanthi Complex, near Surya TV service center, Chettipalayam, Tamil Nadu 641201"}, {"id": 60, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Thondamuthur", "phone": "9894141055", "plusCode": "XRRR+X7 Thondamuthur, Tamil Nadu", "address": "UV complex, Narasipuram Rd, Thondamuthur, Tamil Nadu 641109"}, {"id": 61, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Pappampatti", "phone": "9597552489", "plusCode": "X453+HX Coimbatore, Tamil Nadu", "address": "1/23-E PAPPAMPATTI, Palladam Main Rd, near shanthi feeds, Coimbatore, Tamil Nadu 641016"}, {"id": 62, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Cheran Ma Nagar", "phone": "9080139052", "plusCode": "3227+JR Coimbatore, Tamil Nadu", "address": "No.3, Jeeva Nagar, Cheran ma Nagar, Coimbatore, Tamil Nadu 641035"}, {"id": 63, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Kovilpalayam", "phone": "7339555805", "plusCode": "42MM+XM Kovilpalayam, Tamil Nadu", "address": "Shop no.5 First Floor Bus Stand Complex, Kovilpalayam, Tamil Nadu 641107"}, {"id": 64, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Thennampalayam", "phone": null, "plusCode": "34MF+Q3 Coimbatore, Tamil Nadu", "address": "5/258,A.S.R COMPLEX, THENNAMPALAYAM,ARASUR, Arasur to Annur Link Rd, Coimbatore, Tamil Nadu 641407"}, {"id": 65, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Thudiyalur", "phone": "4224510322", "plusCode": "4WVV+57 Coimbatore, Tamil Nadu", "address": "#188,sri devi nagar,thudiyalur, opposite mtp road, Coimbatore, Tamil Nadu 641020"}, {"id": 69, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "ST Couriers", "area": "Papanaickenpalayam", "phone": "9894451110", "plusCode": "2X8H+6G Pappanaickenpalayam, Tamil Nadu", "address": "5, Kattoor Rd, opp blue dot courier, Siddhapudur, Pappanaickenpalayam, Coimbatore, Tamil Nadu 641037"}, {"id": 70, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Avarapalayam", "phone": "9790339796", "plusCode": "2XFM+QC Coimbatore, Tamil Nadu", "address": "7/7a, MG Rd, Kamaraj Nagar, Avarampalayam, Illango Nagar, Coimbatore, Tamil Nadu 641006"}, {"id": 71, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Tatabad", "phone": null, "plusCode": "2XFM+QC Coimbatore, Tamil Nadu", "address": "51-B, 7th Street, near Bharath Electronics & Appliances, Tatabad, Coimbatore, Tamil Nadu 641012"}, {"id": 72, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Puliakulam", "phone": null, "plusCode": "2X3V+X2 Coimbatore, Tamil Nadu", "address": "66/651, Puliakulam Rd, Puliakulam, Coimbatore, Tamil Nadu 641045"}, {"id": 73, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Gandhipuram", "phone": null, "plusCode": null, "address": "2XF6+74X, Tatapat, Hudco Colony, Tatabad, Gandhipuram, Coimbatore, Tamil Nadu 641012"}, {"id": 74, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": null, "plusCode": "2XC8+X3 Coimbatore, Tamil Nadu", "address": "no 100G,Dr.Radhakrishnan road,Gandhipuram, Coimbatore, Tamil Nadu 641012"}, {"id": 75, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": null, "plusCode": null, "address": "2XF9+2C7, 100 Feet Rd, 1st Street Extension, Gandhipuram, Coimbatore, Tamil Nadu 641012"}, {"id": 76, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9789764270", "plusCode": "2XC9+5Q Gandhipuram, Tamil Nadu", "address": "24/33, 100 Feet Road, near mariamman koil, Gandhipuram, Tamil Nadu 641012"}, {"id": 77, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Peelamedu", "phone": "7708651613", "plusCode": "22G8+6V Coimbatore, Tamil Nadu", "address": "179/3, Avinashi road, KPN Complex opp to GRG School, BR Puram, Peelamedu, Coimbatore, Tamil Nadu 641004"}, {"id": 78, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "7457816603", "plusCode": "2X8R+PF Coimbatore, Tamil Nadu", "address": "515, AVINASHI ROAD, OPP KFC RESTAURANT, NAVA INDIA, Peelamedu, Coimbatore, Tamil Nadu 641037"}, {"id": 79, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9894184168", "plusCode": "22Q7+3W Coimbatore, Tamil Nadu", "address": "126, thaneer panthal road, Peelamedu, Coimbatore, Tamil Nadu 641004"}, {"id": 80, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "7695883787", "plusCode": "22C2+J2 Coimbatore, Tamil Nadu", "address": "R.T.O Office, 32, JEGANATHAN COLONY, near PEELAMEDU, Peelamedu Pudur, Peelamedu, Coimbatore, Tamil Nadu 641004"}, {"id": 81, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Siddhappudur", "phone": "9894451110", "plusCode": "2X8H+6G Pappanaickenpalayam, Tamil Nadu", "address": "5, Kattoor Rd, opp blue dot courier, Siddhapudur, Pappanaickenpalayam, Coimbatore, Tamil Nadu 641037"}, {"id": 82, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": null, "plusCode": "2XCH+RC New Siddhapudur, Tamil Nadu", "address": "202,Basmati complex, Avaram Palayam Rd, Siddhapudur, Balaji Nagar, New Siddhapudur, Tamil Nadu 641044"}, {"id": 83, "rate": "Not answered", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Gopalapuram", "phone": "9843034226", "plusCode": "2X48+8W Coimbatore, Tamil Nadu", "address": "35A National Complex Grey Town, Dr.Nanjappa Road Addis Street, Head Quarters Road, Government Arts College Rd, Gopalapuram, Coimbatore, Tamil Nadu 641018"}, {"id": 84, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "8122895689", "plusCode": "2X49+F3 Coimbatore, Tamil Nadu", "address": "HM Complex, 9/14, Addis St, Srikanth Extension, Grey Town, ATT Colony, Gopalapuram, Coimbatore, Tamil Nadu 641018"}, {"id": 85, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Townhall", "phone": "9791892039", "plusCode": "XXV4+VP Coimbatore, Tamil Nadu", "address": "594, Raja St, near Registrar Office, Town Hall, Coimbatore, Tamil Nadu 641001"}, {"id": 86, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9003435470", "plusCode": "2X24+34 Coimbatore, Tamil Nadu", "address": "995, Sukrawarpet St, Town Hall, Coimbatore, Tamil Nadu 641001"}, {"id": 87, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Ram Nagar", "phone": "9715446976", "plusCode": "2X66+PH Coimbatore, Tamil Nadu", "address": "Ram Nagar, Coimbatore, Tamil Nadu 641009"}, {"id": 88, "rate": "Not answered", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "4222266666", "plusCode": "2X56+FP Coimbatore, Tamil Nadu", "address": "222, Vivekananda Rd, Ram Nagar, Coimbatore, Tamil Nadu 641009"}, {"id": 89, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "RS Puram", "phone": null, "plusCode": "XXX2+WQ Coimbatore, Tamil Nadu", "address": "282, Diwan Bahadur Rd, near KALPANA STUDIO, R.S. Puram, Coimbatore, Tamil Nadu 641002"}, {"id": 90, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "8778653396", "plusCode": "2X52+2H Coimbatore, Tamil Nadu", "address": "27, Kannusamy Rd, R.S. Puram, Coimbatore, Tamil Nadu 641002"}, {"id": 91, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": null, "plusCode": "2X57+9W Coimbatore, Tamil Nadu", "address": "15B, Ranga Konar St, Kattoor Main, Ram Nagar, Coimbatore, Tamil Nadu 641009"}, {"id": 92, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "4461266666", "plusCode": null, "address": "XXX3+WG7, SH 164, R.S. Puram, Coimbatore, Tamil Nadu 641001"}, {"id": 93, "rate": "Not answered", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9629969798", "plusCode": "2X73+XP Coimbatore, Tamil Nadu", "address": "Shop No. 1092, Mettupalayam Rd, opposite Canara Bank, near Dr. Agarwal Eye hospital, Vadakovai, R.S. Puram, Coimbatore, Tamil Nadu 641002"}, {"id": 94, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Ramanathapuram", "phone": "9944333466", "plusCode": "X2W2+PG Ramanathapuram, Tamil Nadu", "address": "NO-734, Pazhamudhir Nilayam, Trichy Rd, near cityunion bank, T Nagar, Ramanathapuram, Coimbatore, Tamil Nadu 641045"}, {"id": 95, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "4224350565", "plusCode": "XXXW+44 Ramanathapuram, Tamil Nadu", "address": "TJ Complex, 115, Pankaja Mills Rd, Olymbus, Ramasamy Nagar, Ramanathapuram, Coimbatore, Tamil Nadu 641045"}, {"id": 96, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Ganapathy", "phone": "4222537454", "plusCode": "2XWP+X4 Coimbatore, Tamil Nadu", "address": "21,, Athipalayam Rd, Ganapathy, Coimbatore, Tamil Nadu 641006"}, {"id": 97, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": null, "plusCode": "2XXF+F3 Coimbatore, Tamil Nadu", "address": "Nehru Ave, Kappini Gounder Layout, Poosaripalayam, Manikarampalayam, Ganapathy, Coimbatore, Tamil Nadu 641006"}, {"id": 98, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": null, "plusCode": "2XQJ+RJ Coimbatore, Tamil Nadu", "address": "No:7/7A, Sathy Rd, Opp. CMS School, KRG Nagar, Ganapathy, Coimbatore, Tamil Nadu 641006"}, {"id": 99, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Neelikonampalayam", "phone": null, "plusCode": "227M+HJ Neelikonampalayam, Tamil Nadu", "address": "19a, Angammal layout, Neelikonampalayam, Tamil Nadu 641033"}, {"id": 100, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Mudilipalayam", "phone": "4422666666", "plusCode": "2X4V+33 Coimbatore, Tamil Nadu", "address": "757, Avinashi Road, Avinashi Road, Opposite SBI, Mudilipalyam, Coimbatore, Tamil Nadu 641045"}, {"id": 101, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Selvapuram", "phone": "9025033754", "plusCode": "XWPW+VF Coimbatore, Tamil Nadu", "address": "3/539/1, Sundakkamuthur Bypass Rd, Savithrinagar, Balaji Avenue, Selvapuram South, Selvapuram, Coimbatore, Tamil Nadu 641026"}, {"id": 102, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Anna Salai", "phone": "9894476666", "plusCode": "2X4G+G9 Coimbatore, Tamil Nadu", "address": "Anna Silai, Coimbatore, Tamil Nadu 641018"}, {"id": 103, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Sivananda Colony", "phone": "9080007375", "plusCode": "22C5+M7 Coimbatore, Tamil Nadu", "address": "22/ 8c, Sivananda Colony Rd, HUDCO Colony, Coimbatore, Tamil Nadu 641004"}, {"id": 104, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Edayarpalayam", "phone": "9585438880", "plusCode": "2WQJ+Q9 Coimbatore, Tamil Nadu", "address": "1/5, opp. ponnaiya kalyana mandapam, Lakshmi Nagar, Edayarpalayam, Coimbatore, Tamil Nadu 641025"}, {"id": 105, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Saravanampatti", "phone": "4461266666", "plusCode": "32G3+G2 Coimbatore, Tamil Nadu", "address": "Check Post, Shop No.4, Opp HDFC bank, Sathy Rd, Saravanampatti, Coimbatore, Tamil Nadu 641035"}, {"id": 106, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": null, "plusCode": "3282+VP Coimbatore, Tamil Nadu", "address": "bus stop, Sathy Rd, Amman Kovil, ammnkovil, Saravanampatti, Coimbatore, Tamil Nadu 641035"}, {"id": 107, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "8190993312", "plusCode": null, "address": "3X3V+4C9, Ramakrishnapuram, Saravanampatti, Siranandha Puram, Coimbatore, Tamil Nadu 641006"}, {"id": 108, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Rathinapuri", "phone": "9941008557", "plusCode": "2XX8+5G Coimbatore, Tamil Nadu", "address": "26-a,nnarayanasamy street, kannappanagar, rathinapuri, near police station, Coimbatore, Tamil Nadu 641006"}, {"id": 109, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9080007375", "plusCode": "2XG8+9G Coimbatore, Tamil Nadu", "address": "26-a,nnarayanasamy street, kannappanagar, rathinapuri, near police station, Coimbatore, Tamil Nadu 641006"}, {"id": 110, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Koundampalayam", "phone": "8870019996", "plusCode": null, "address": "2WVW+VQQ, Edayarpalayam - Koundampalayam Rd, Koundampalayam, Coimbatore, Tamil Nadu 641030"}, {"id": 111, "rate": "Lack of information", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Ukkadam", "phone": "7373557017", "plusCode": "XXP7+6F Coimbatore, Tamil Nadu", "address": "1, Al-Ameen Colony, Bilal Nagar, Ukkadam, Coimbatore, Tamil Nadu 641023"}, {"id": 112, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "PN Pudur", "phone": "9894040444", "plusCode": "2W8F+FQ P N Pudur, Tamil Nadu", "address": "55, Marudhamalai Rd, Aishwarya Nagar, P N Pudur, Coimbatore, Tamil Nadu 641041"}, {"id": 113, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Singanullur", "phone": "7871572030", "plusCode": "222J+8M Singanallur, Tamil Nadu", "address": "RVN Complex, Old No.494, New No.501, 501, Trichy Rd, opposite to city union bank, Agraharam, Singanallur, Tamil Nadu 641005"}, {"id": 114, "rate": 90.0, "pickup": "yes", "drop": "yes", "timeInDays": "2 - 3", "packing": "no", "company": null, "area": null, "phone": "9080357581", "plusCode": "X2XJ+FQ Singanallur, Tamil Nadu", "address": "areodram road,, Singanallur, Tamil Nadu 641005"}, {"id": 115, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": null, "plusCode": "X2XG+RH Singanallur, Tamil Nadu", "address": "585, Kongunadu Complex, Uzhavar Sandhai, Trichy Rd, opp. site, Vasanth Nagar, Singanallur, Tamil Nadu 641005"}, {"id": 116, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Saibaba Colony", "phone": "9750500103", "plusCode": "2WFW+CH Saibaba Colony, Jawahar Nagar, Coimbatore, Tamil Nadu", "address": "Bharathi Raja Complex, Raja Annamalai Rd, next to angappa school, Nesavaalar Colony, Saibaba Colony, Coimbatore, Tamil Nadu 641011"}, {"id": 117, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "4224385258", "plusCode": "2WGR+CQ Coimbatore, Tamil Nadu", "address": "320, ARPEE Center, complex, NSR Rd, R. P. Centre, Saibaba Colony, Coimbatore, Tamil Nadu 641038"}, {"id": 118, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Chinnampalayam", "phone": "9042084000", "plusCode": "22P7+FJ Rangasamy Gounden Pudur, Coimbatore, Tamil Nadu", "address": "675, 9, Avinashi Rd, Maheshwari Nagar, Rangasamy Gounden Pudur, Chinnampalayam, Coimbatore, Tamil Nadu 641004"}, {"id": 119, "rate": 100.0, "pickup": "yes", "drop": "yes", "timeInDays": 2.0, "packing": "no", "company": null, "area": "Sundarapuram", "phone": "9788824584", "plusCode": "XX4F+74 Sundarapuram, Coimbatore, Kurichi, Tamil Nadu", "address": "Pollachi Main Rd, Sundarapuram, Coimbatore, Kurichi, Tamil Nadu 641024"}, {"id": 120, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Cheran Ma Nagar", "phone": "9080139052", "plusCode": "3227+JR Coimbatore, Tamil Nadu", "address": "No.3, Jeeva Nagar, Cheran ma Nagar, Coimbatore, Tamil Nadu 641035"}, {"id": 121, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Chinnavedampetti", "phone": "9245277749", "plusCode": "3X6M+G5 Coimbatore, Tamil Nadu", "address": "Athipalayam Rd, Subramaniyanagar, Chinnavedampatti, Coimbatore, Tamil Nadu 641049"}, {"id": 122, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Uppilipalyam", "phone": "9585590701", "plusCode": "226F+GJ Coimbatore, Tamil Nadu", "address": "291 Kamarajar Rd,Opp Karthikeya Mill, near E.S.I Hospital,Uppilipalayam post, Coimbatore, 641015, Tamil Nadu 641015"}, {"id": 123, "rate": 110.0, "pickup": "yes", "drop": "yes", "timeInDays": 3.0, "packing": "no", "company": null, "area": "Vedapatti", "phone": "9789288088", "plusCode": "2V2W+72 Coimbatore, Tamil Nadu", "address": "VKV Sree Nagar, Thondamuthur Rd, Kurumbapalayam, Vedapatti, Coimbatore, Tamil Nadu 641007"}, {"id": 124, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Kalapatti", "phone": null, "plusCode": null, "address": "32HP+3GQ, Kalapatti, Coimbatore, Tamil Nadu 641048"}, {"id": 125, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9786269053", "plusCode": "22VW+JV Coimbatore, Tamil Nadu", "address": "ST COURIER , RASIGA AGENCY, NERA SUGAM ORGANICS, Goldwins, Coimbatore, Tamil Nadu 641014"}, {"id": 126, "rate": "Does no exist", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Vellalur", "phone": "9171622922", "plusCode": "X2JM+59 Vellalore, Tamil Nadu", "address": "Vinayagar Nagar, ELGI Nagar, Vellalore, Tamil Nadu 641111"}, {"id": 127, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Kovaipudur", "phone": "8248886395", "plusCode": "WWVM+4X Coimbatore, Tamil Nadu", "address": "103A, opp. KVB BANK, V Block, Coimbatore, Kovaipudur, Tamil Nadu 641042"}, {"id": 128, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Vellakinar", "phone": "9944870561", "plusCode": "3W7V+VQ Coimbatore, Tamil Nadu", "address": "12/301A Thiruvalluvar street, street, beside Dhanalakshmi hotel, Vellakinar, Coimbatore, Tamil Nadu 641029"}, {"id": 129, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Veerakeralam", "phone": null, "plusCode": "2W47+X9 Veerakeralam, Tamil Nadu", "address": "34A, Devar St, Veerakeralam, Coimbatore, Tamil Nadu 641007"}, {"id": 130, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Ganesh Nagar", "phone": "7904221340", "plusCode": "3W5V+PV Coimbatore, Tamil Nadu", "address": "VKA Complex, Mettupalayam Rd, Ganesh Nagar, Gounder Mills, Coimbatore, Tamil Nadu 641029"}, {"id": 131, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Ellanrthottam", "phone": "8220587350", "plusCode": "22H2+QR Coimbatore, Tamil Nadu", "address": "VK Rd, near Railway overbridge Biriyani shop complex, Ellanthottam, Coimbatore, Tamil Nadu 641004"}, {"id": 132, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Rakkipalayam", "phone": null, "plusCode": "4W2Q+8G Coimbatore, Tamil Nadu", "address": "NO 1/314, THIRUMALA AVENUE, opp. to VASAN EYE CARE, Thoppampatti Pirivu, Rakkipalayam, Coimbatore, Tamil Nadu 641017"}, {"id": 133, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Madhukarai", "phone": "9698003777", "plusCode": "WX73+F4 Madukkarai, Tamil Nadu", "address": "madhukarai, Madukkarai, Tamil Nadu 641105"}, {"id": 134, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Ellamal layouts", "phone": "9092192118", "plusCode": "223X+G3 Coimbatore, Tamil Nadu", "address": "opposite to Hotel Annavasal, near to MuthuVilas Sweets, Ellammal Layout, Coimbatore, Tamil Nadu 641016"}, {"id": 135, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Sulur", "phone": "9952315770", "plusCode": "24G9+3P Coimbatore, Tamil Nadu", "address": "sulur,, Trichy Rd, Coimbatore, Tamil Nadu 641402"}, {"id": 136, "rate": "Lack of infromation", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Chettipalayam", "phone": "9025335369", "plusCode": "W28Q+6X Chettipalayam, Tamil Nadu", "address": "14/22, Palladam - Cochin Frontier Rd, Chettipalayam, Tamil Nadu 641201"}, {"id": 137, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Thondamuthur", "phone": "4224398301", "plusCode": "XRQR+X9 Thondamuthur, Tamil Nadu", "address": "KTR Nagar, Thondamuthur, Tamil Nadu 641109"}, {"id": 138, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "NAVA INDIA", "phone": null, "plusCode": "2X8R+V2 Coimbatore, Tamil Nadu", "address": "143-a, Nava India Rd, main road, Coimbatore, Tamil Nadu 641037"}, {"id": 139, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Periyanayakanpalayan", "phone": "9698003777", "plusCode": "4WXV+WJ Coimbatore, Tamil Nadu", "address": "periyanayakan palayam, Mettupalayam Road, Coimbatore, Tamil Nadu 641020"}, {"id": 143, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "AKR Parcel Service", "area": "Sukrawar Pettai", "phone": "9442223035", "plusCode": "2X24+PW Coimbatore, Tamil Nadu", "address": "No.75, Theppakulam Street 3, Sukrawar Pettai, Theppakulam Maidhanam, Coimbatore, Tamil Nadu 641001"}, {"id": 144, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Town Hall", "phone": "9443138882", "plusCode": "XXW4+83 Coimbatore, Tamil Nadu", "address": "MM Complex, 214, Thomas Veedi, Town Hall, Coimbatore, Tamil Nadu 641001"}, {"id": 145, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Ram Nagar", "phone": "9442105799", "plusCode": "2X47+QG Coimbatore, Tamil Nadu", "address": "161, Mariappa Konar St, Kalieshwara Nagar, Kattoor, Ram Nagar, Coimbatore, Tamil Nadu 641009"}, {"id": 146, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Tatabad", "phone": "9442105575", "plusCode": "2X96+GH Coimbatore, Tamil Nadu", "address": "No.222, Dr.Rajendraprasath Street 100 Ft Road, Tatabad, Coimbatore, Tamil Nadu 641012"}, {"id": 147, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Saibaba Colony", "phone": "9489861759", "plusCode": "2WFV+26 Coimbatore, Tamil Nadu", "address": "No.47C, Bharathi Park 3rd Cross, Alagesan Rd, near BSNL Office, Saibaba Colony, Coimbatore, Tamil Nadu 641011"}, {"id": 148, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Ukkadam", "phone": "9487909681", "plusCode": "XXR2+HQ Coimbatore, Tamil Nadu", "address": "No.234, Vysiyal Street, Ukkadam, Coimbatore, Tamil Nadu 641001"}, {"id": 149, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Pappanaickenpalayam", "phone": "9443277850", "plusCode": "2X7P+Q4 Pappanaickenpalayam, Tamil Nadu", "address": "1043-C, Bharathiyar Rd, Pudur, Pappanaickenpalayam, Coimbatore, Tamil Nadu 641037"}, {"id": 150, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Velandipalayam", "phone": "9443300821", "plusCode": "2WGM+MQ Velandipalayam, Tamil Nadu", "address": "No.27, Anna Street, Venkittapuram Rd, Velandipalayam, Tamil Nadu 641025"}, {"id": 151, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Ganapathy", "phone": "9442190599", "plusCode": "2XWJ+H2 Coimbatore, Tamil Nadu", "address": "No.394, Nehru Nagar, 5th Street Behind CMS School, near Einsten Puplic School, Ganapathy Housing Unit, Coimbatore, Tamil Nadu 641006"}, {"id": 152, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Ramanathapuram", "phone": "4272225555", "plusCode": "XXVV+HF Ramanathapuram, Tamil Nadu", "address": "16/2, 80 Feet Rd, Papammal Layout, VOC Nagar, Olympus, Ramanathapuram, Coimbatore, Tamil Nadu 641045"}, {"id": 153, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Peelamedu", "phone": "9442223057", "plusCode": "22P3+74 Coimbatore, Tamil Nadu", "address": "No.62167, Hudco Colony, Peelamedu, Murugan Nagar, Coimbatore, Tamil Nadu 641004"}, {"id": 154, "rate": "450 (not clear)", "pickup": "no", "drop": "no", "timeInDays": "3 - 4", "packing": "no", "company": null, "area": "Vadavalli", "phone": "9489861792", "plusCode": "2W95+FH Coimbatore, Tamil Nadu", "address": "No.2/8, RSM Complex, Siruvani Rd, Linganoor, Vadavalli, Coimbatore, Tamil Nadu 641041"}, {"id": 155, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Chinnavedampetti", "phone": "4272225555", "plusCode": "32H3+M3 Coimbatore, Tamil Nadu", "address": "307, Athipalayam Road, Athipalayam Road, Near Tasmac, Chinnavedampatti, Coimbatore, Tamil Nadu 641035"}, {"id": 156, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9150059076", "plusCode": "3X7M+VH Coimbatore, Tamil Nadu", "address": "D, 1/15, Athipalayam Rd, Asiriyar Colony, Chinnavedampatti, Coimbatore, Tamil Nadu 641049"}, {"id": 157, "rate": "Not answered", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Singanallur", "phone": "9443263868", "plusCode": "222R+RV Coimbatore, Tamil Nadu", "address": "No.178, Textool Layout 1, near Chinnadurai Grocery Store, Vivekanandha Nagar, Singanallur, Coimbatore, Tamil Nadu 641005"}, {"id": 158, "rate": "Switch off", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Kurichi", "phone": "9443300858", "plusCode": "WXXH+46 Kurichi, Tamil Nadu", "address": "No.1/190A, 2nd Street, Pollachi Main Rd, LIC Colony, SIDCO Private Industrial Estate, Kurichi, Tamil Nadu 641023"}, {"id": 159, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "GN Mills", "phone": "9442145282", "plusCode": "3W4V+8G Coimbatore, Tamil Nadu", "address": "No.9/10, MKG Layout, ITI Backside G.N Mills, Post, Coimbatore, Tamil Nadu 641029"}, {"id": 160, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Civil Aerodrome Post", "phone": "9443376710", "plusCode": "323W+GW Coimbatore, Tamil Nadu", "address": "No.6, Sri Krishna Garden, Nehru St, opp. Tamilnadu Housing Board, Civil Aerodrome Post, Po, Coimbatore, Tamil Nadu 641014"}, {"id": 161, "rate": "Industrial goods only", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Thudiyalur", "phone": "9443300854", "plusCode": "3WFR+V9 Thudiyalur, Tamil Nadu", "address": "Kannan Departmental Store Backside, No.105-B6, Mettupalayam Rd, Viswanathapuram, Thudiyalur, Tamil Nadu 641034"}, {"id": 162, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Papampatti", "phone": "9489862761", "plusCode": "235F+7V Coimbatore, Tamil Nadu", "address": "5/30, Teachers Colony Bharathipuram Post, Pirivu, near Papampatti, Coimbatore, Tamil Nadu 641103"}, {"id": 163, "rate": "Lack of infromation", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Okkillipalayam", "phone": "9489933156", "plusCode": "V2W7+7J Palladam, Tamil Nadu", "address": "No.33/1A, Manikandan Nagar, Road, Palladam, Okkillipalayam, Tamil Nadu 641032"}, {"id": 167, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "Professional Couries", "area": "Ram Nagar", "phone": "9626250204", "plusCode": "2X86+JF Coimbatore, Tamil Nadu", "address": "No.452, 453, Vivekananda Rd, near KMCH city center, Ram Nagar, Coimbatore, Tamil Nadu 641009"}, {"id": 168, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "6374806535", "plusCode": "2X58+J7 Coimbatore, Tamil Nadu", "address": "29-32, Kaalingarayar Street, near Geetha Canteen, opp. BSNL, Ram Nagar, Coimbatore, Tamil Nadu 641009"}, {"id": 169, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Town Hall", "phone": null, "plusCode": "XXR5+XQ Coimbatore, Tamil Nadu", "address": "10, 1st Floor, Vysial St, Oppanakara St, Prakasam, Town Hall, Coimbatore, Tamil Nadu 641001"}, {"id": 170, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "RS Puram", "phone": "9629969798", "plusCode": "2X73+XP Coimbatore, Tamil Nadu", "address": "Shop No. 1092, Mettupalayam Rd, opposite Canara Bank, near Dr. Agarwal Eye hospital, Vadakovai, R.S. Puram, Coimbatore, Tamil Nadu 641002"}, {"id": 171, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9843245022", "plusCode": "2W5V+7V Coimbatore, Tamil Nadu", "address": "Thadagam Main Rd, Subramaniapuram, R.S. Puram, Coimbatore, Tamil Nadu 641002"}, {"id": 172, "rate": 140.0, "pickup": "no", "drop": "no", "timeInDays": "4 - 5", "packing": "no", "company": null, "area": "Gopalapuram", "phone": "9843206202", "plusCode": "2X2C+2G Coimbatore, Tamil Nadu", "address": "10, Annapoorna Peoples Park, 26, Government Arts College Rd, Opposite Court, Gopalapuram, Coimbatore, Tamil Nadu 641018"}, {"id": 173, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Shanmuga Nagar", "phone": "4223505555", "plusCode": "XXRH+VR Coimbatore, Tamil Nadu", "address": "56, 2, Sungam Bypass Rd, near KTM Showroom, Shanmuga Nagar, Coimbatore, Tamil Nadu 641018"}, {"id": 174, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Gandhipuram", "phone": "9843156696", "plusCode": "2X96+WF Coimbatore, Tamil Nadu", "address": "538, 5th St, opposite to KalayanJewellers, Tatabad, Gandhipuram, Coimbatore, Tamil Nadu 641012"}, {"id": 175, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9843848020", "plusCode": "2XC7+GP Gandhipuram, Tamil Nadu", "address": "A 12, 9th St, Tatabad, Gandhipuram, Coimbatore, Tamil Nadu 641012"}, {"id": 176, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Siddhapudur", "phone": "9655233022", "plusCode": "2X8F+HG New Siddhapudur, Tamil Nadu", "address": "204, Bharathiyar Rd, Siddhapudur, New Siddhapudur, Coimbatore, Tamil Nadu 641044"}, {"id": 177, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Saibaba Colony", "phone": "9843121514", "plusCode": "2XG2+94 Coimbatore, Tamil Nadu", "address": "3, 2, Sree Narayana Guru Rd, Nesavaalar Colony, Saibaba Colony, Coimbatore, Tamil Nadu 641011"}, {"id": 178, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9843121514", "plusCode": null, "address": "2WFW+G49, Old No.7,New No.5, Raghupathy lay out Ist, 7/90, cross, Jawahar Nagar, Saibaba Colony, Coimbatore, Tamil Nadu 641011"}, {"id": 179, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Masakalipalayam", "phone": "9843827527", "plusCode": null, "address": "2299+XMF, Masakaalipalayam Link Rd, Periyar Nagar, Lakshmipuram, Masakali Palayam, Coimbatore, Tamil Nadu 641004"}, {"id": 180, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9843827527", "plusCode": "2XCX+PG Peelamedu, Maheshwari Nagar, Coimbatore, Tamil Nadu", "address": "12A, Avinashi Rd, Peelamedu, Masakalipalayam, Coimbatore, Tamil Nadu 641004"}, {"id": 181, "rate": "Not answering", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Koundapalayam", "phone": "9787724100", "plusCode": "2WWW+6Q Coimbatore, Tamil Nadu", "address": "KKG Complex, Bus Stop, Mettupalayam Rd, Koundampalayam, Coimbatore, Tamil Nadu 641030"}, {"id": 182, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Pudur", "phone": "9047028885", "plusCode": "2X6P+9X Coimbatore, Tamil Nadu", "address": "456, 456, Puliakulam Rd, New Dhamu Nagar, Pudur, Coimbatore, Tamil Nadu 641037"}, {"id": 183, "rate": "Not answering", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Kuniyamuthur", "phone": "9043521416", "plusCode": "XX53+WR Kuniyamuthur, Tamil Nadu", "address": "Sri Ram Complex, Palakkad - Coimbatore Rd, Sai Garden, Kuniyamuthur, Tamil Nadu 641008"}, {"id": 184, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Ganapathy", "phone": "9943026755", "plusCode": "2XRJ+5V Coimbatore, Tamil Nadu", "address": "NH948 Opp. CMS School, Ganapathy Housing Unit, Coimbatore, Tamil Nadu 641006"}, {"id": 185, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9843398175", "plusCode": null, "address": "2XW4+6Q7, ANNAIAPPAN STREET, NALLAMPALAYAM, Ganapathy, Coimbatore, Tamil Nadu 641006"}, {"id": 186, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9943026755", "plusCode": "2XRG+XM Coimbatore, Tamil Nadu", "address": "Amman Complex, Bharathiyar Rd, Maniyakarampalayam, Karpaga Vinyaha Nagar, Ganapathy, Coimbatore, Tamil Nadu 641006"}, {"id": 187, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Vadavalli", "phone": "9843433003", "plusCode": "2WH3+87 Coimbatore, Tamil Nadu", "address": "Shop No. 1, First floor, Vasinavi Complex,Opp to Anandhaas ,, Maruthamalai Main Road, Vadavalli, Coimbatore, Tamil Nadu 641041"}, {"id": 188, "rate": "Not answering", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9843433003", "plusCode": "2WF4+M3 Marutha Nagar, Senniveerampalayam, Coimbatore, Tamil Nadu", "address": "Shop No.533-C, Vadavalli-Thondamuthur Rd, Marutha Nagar, Vadavalli, Tamil Nadu 641041"}, {"id": 189, "rate": "Switch off", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Singanallur", "phone": "9843859848", "plusCode": "X2XG+RH Singanallur, Tamil Nadu", "address": "Kongunadu Complex, 585, Trichy Rd, Vasanth Nagar, Singanallur, Tamil Nadu 641005"}, {"id": 190, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Ramanathapuram", "phone": null, "plusCode": "XXXW+66 Ramanathapuram, Tamil Nadu", "address": "96-97, Pankaja Mills Rd, opp. Bsnl Office, Palaniappa Nagar, Sowripalayam Pirivu, Ramanathapuram, Coimbatore, Tamil Nadu 641045"}, {"id": 191, "rate": 120.0, "pickup": "no", "drop": "no", "timeInDays": "3 - 4", "packing": "no", "company": null, "area": "Perur", "phone": "9843140035", "plusCode": "XWF7+VP Coimbatore, Tamil Nadu", "address": "68 a, Siruvani Main Rd, Perur, Coimbatore, Tamil Nadu 641010"}, {"id": 192, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Kovaipudur", "phone": null, "plusCode": "WWXM+68 Kovaipudur, Tamil Nadu", "address": "S-75, Maha Complex, Garuda Ave, S - Block, Kovaipudur, Tamil Nadu 641042"}, {"id": 193, "rate": 110.0, "pickup": "yes", "drop": "yes", "timeInDays": "3 - 4", "packing": "no", "company": null, "area": "Podanur", "phone": "9843521416", "plusCode": "WXXH+W3 Coimbatore, Tamil Nadu", "address": "641024, Sundarapuram, LIC Colony, Coimbatore, Podanur, Tamil Nadu 641023"}, {"id": 194, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Thudiyalur", "phone": "9585533868", "plusCode": null, "address": "3WGR+XQ2, Cheran Colony, Thudiyalur, Kurudampalayam, Tamil Nadu 641034"}, {"id": 195, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "4222642334", "plusCode": "2WWW+3V Coimbatore, Tamil Nadu", "address": "205, M.T.P. Road, M.T.P Road, Near Murugan Theater, Thudiyalur, Coimbatore, Tamil Nadu 641030"}, {"id": 196, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "TVS Nagar", "phone": "9843121514", "plusCode": null, "address": "2WWF+24R, Thadagam Main Rd, PMR Nagar, TVS Nagar, Coimbatore, Tamil Nadu 641025"}, {"id": 197, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Gounder Mills", "phone": null, "plusCode": "3W5W+J4 Coimbatore, Tamil Nadu", "address": "B5, Mayura Complex, Bus Stop, Mettupalayam Rd, Gounder Mills, Coimbatore, Tamil Nadu 641029"}, {"id": 198, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "BK Pudur", "phone": "8825524004", "plusCode": "WWQW+W4 Coimbatore, Tamil Nadu", "address": "Splash 9/1,2,3, SPLASH STUDENTS PRINT, Rajkamal Complex, Kovaipudur Main Rd, opp. V L B ARTS COLLEGE, Reevatsa Hillview, BK Pudur, Coimbatore, Kuniyamuthur, Tamil Nadu 641042"}, {"id": 199, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Periyar Nagar", "phone": null, "plusCode": "324Q+6G Coimbatore, Tamil Nadu", "address": "132a, Kalapatti Main Rd, Periyar Nagar, Nehru Nagar West, Coimbatore, Tamil Nadu 641048"}, {"id": 200, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Pappampatti", "phone": "9843818463", "plusCode": "236J+36 Coimbatore, Tamil Nadu", "address": "48-B / 3-1, Vaithiyar Complex, Trichy Road, Pappampatty, Pirivu, Coimbatore, Tamil Nadu 641103"}, {"id": 201, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Ondipudur", "phone": null, "plusCode": null, "address": "223X+J3V, Ellammal Layout, Ondipudur, Coimbatore, Tamil Nadu 641016"}, {"id": 202, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Kuppicihipalayam", "phone": "9786690212", "plusCode": "5W4X+39 Periyanaickenpalayam, Tamil Nadu", "address": "Palamalai Rd, Kuppichipalayam, Periyanaickenpalayam, Tamil Nadu 641020"}, {"id": 203, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Sulur", "phone": "9943051252", "plusCode": "24GG+QR Coimbatore, Tamil Nadu", "address": "5 /19 E, 1 & 2, Trichy Main Road, Sulur, Coimbatore, Tamil Nadu 641402"}, {"id": 204, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Narasimhanaikenpalayam", "phone": "4262261105", "plusCode": "4W7P+R9 Narasimhanaickenpalayam, Tamil Nadu", "address": "No. 10/368, Ooty Road, Gudalur Bazar, Nilgiris, Opposite Vinayagar Temple, Narasimhanaickenpalayam, Tamil Nadu 641031"}, {"id": 205, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Saravanampatti", "phone": "9043994491", "plusCode": "3WMQ+JQ Coimbatore, Tamil Nadu", "address": "1A, Thudiyalur Road, Thudiyalur Road, Near Mosque, Saravanam Patti, Coimbatore, Tamil Nadu 641017"}, {"id": 206, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Madukkarai", "phone": null, "plusCode": "WX37+J2 Madukkarai, Tamil Nadu", "address": "12/123, Anbu Nagar, Madukkarai, Tamil Nadu 641105"}, {"id": 207, "rate": 130.0, "pickup": "yes", "drop": "yes", "timeInDays": "3 -5", "packing": "no", "company": null, "area": "Malumichampatti", "phone": "9843521416", "plusCode": null, "address": "WX3X+CCV, Pollachi Main Rd, near pavilam jewelry, Guruvayur Nagar, Malumichampatti, Tamil Nadu 641050"}, {"id": 208, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Chinthamanipudur", "phone": null, "plusCode": "235G+8G Coimbatore, Tamil Nadu", "address": "62-C, Trichy Rd, Post, Chinthamanipudur, Coimbatore, Tamil Nadu 641103"}, {"id": 209, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "PN Puram", "phone": "4222314802", "plusCode": "3VRJ+57 Coimbatore, Tamil Nadu", "address": "Sf-9, Phase II, Pn Puram, Coimbatore, Tamil Nadu 641108"}, {"id": 213, "rate": 80.0, "pickup": "no", "drop": "no", "timeInDays": 2.0, "packing": "no", "company": "Franch Express network", "area": "Gopalapuram", "phone": "4222303728", "plusCode": "2X48+CV Coimbatore, Tamil Nadu", "address": "Shop No.35, National Complex, Grey Town Rd, Srikanth Extension, Grey Town, ATT Colony, Gopalapuram, Coimbatore, Tamil Nadu 641018"}, {"id": 214, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Maniyakarampalayam", "phone": "9003685538", "plusCode": "2X58+M8 Coimbatore, Tamil Nadu", "address": "049, S- Bend, near Camford School, Cbe - 641, Ram Nagar, Maniyakarampalayam, Coimbatore, Tamil Nadu 641009"}, {"id": 215, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "TownHall", "phone": "4466332333", "plusCode": "3WHR+WM Coimbatore, Tamil Nadu", "address": "10/68, Sukrawarpet, Near Lala Sweets, Town Hall, Coimbatore, Tamil Nadu 641034"}, {"id": 216, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Saibaba Colony", "phone": "7418959777", "plusCode": "2WGV+HJ Saibaba Colony, Jawahar Nagar, Coimbatore, Tamil Nadu", "address": "Valargangai Complex, 390,, NSR Rd, Saibaba Colony, Tamil Nadu 641011"}, {"id": 217, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Peelamedu", "phone": "9944968088", "plusCode": "22F4+3F Coimbatore, Tamil Nadu", "address": "PSG Estate Colony, Peelamedu, Coimbatore, Tamil Nadu 641004"}, {"id": 218, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Ganapathy", "phone": "9894677735", "plusCode": "2X98+GR Gandhipuram, Tamil Nadu", "address": "232K, 5, 6th Cross Rd, Ganapathy, Gandhipuram, Coimbatore, Tamil Nadu 641012"}, {"id": 219, "rate": "Not answering", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "RS Puram", "phone": "7871561442", "plusCode": "2X52+Q7 Coimbatore, Tamil Nadu", "address": "51/28 AB,, Diwan Bahadur Rd, R.S. Puram, Coimbatore, Tamil Nadu 641002"}, {"id": 220, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Gandhipuram", "phone": "4226460486", "plusCode": "2X98+R8 Gandhipuram, Tamil Nadu", "address": "Madani Complex, Dr Rajendra Prasad Rd, Gandhipuram, Coimbatore, Tamil Nadu 641012"}, {"id": 221, "rate": 80.0, "pickup": "yes", "drop": "yes", "timeInDays": 3.0, "packing": "no", "company": null, "area": "Townhall", "phone": "9042006994", "plusCode": "XXX5+J9 Coimbatore, Tamil Nadu", "address": "#775, Oppanakara St., near punjab national bank, Town Hall, Coimbatore, Tamil Nadu 641001"}, {"id": 222, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Uppilipalayam", "phone": "9894550336", "plusCode": "229F+M2 Uppilipalayam, Tamil Nadu", "address": "CANARA BANK building, LIONs stop, Masakalipalayam Link Rd, opp. to ALOFT HOTEL, Periyar Nagar, Lakshmi Ammal Layout, Lakshmipuram, Uppilipalayam, Coimbatore, Tamil Nadu 641004"}, {"id": 226, "rate": "Not answering", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "Tarckon Couriers Pvt Ltd", "area": "Gopalapuram", "phone": "4224206112", "plusCode": "2X28+R5 Coimbatore, Tamil Nadu", "address": "Sri Thevar Complex, 153, Avinashi Rd, Below Flyover, CBE, Gopalapuram, Coimbatore, Tamil Nadu 641018"}, {"id": 227, "rate": 90.0, "pickup": "yes", "drop": "yes", "timeInDays": 4.0, "packing": "yes", "company": null, "area": "Saibaba Colony", "phone": "9894052258", "plusCode": "2WGV+F6 Saibaba Colony, Jawahar Nagar, Coimbatore, Tamil Nadu", "address": "Shop No. 320 AAR PEE centre, NSR Rd, Saibaba Colony, Tamil Nadu 641011"}, {"id": 228, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9385353323", "plusCode": "2XC2+2M Coimbatore, Tamil Nadu", "address": "62, Bharathi Park Rd, opp. to Avinashilingam University, Ramalingam Colony, Saibaba Colony, Coimbatore, Tamil Nadu 641043"}, {"id": 232, "rate": "Not answering", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "Shree Anjaani Courier Service pvt ltd", "area": "RS Puram", "phone": "4223131686", "plusCode": "2X42+PP Coimbatore, Tamil Nadu", "address": "No.22 Ground Floor, E Arokiasamy Rd, near Annapoorna Hotel, R.S. Puram, Coimbatore, Tamil Nadu 641002"}, {"id": 236, "rate": "Number does not exist", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "Shree Tirupati Courier Service Pvt Ltd", "area": "RS Puram", "phone": "7457816603", "plusCode": "2X42+XF Coimbatore, Tamil Nadu", "address": "BIG BELL COMPLEX, 106/20, Diwan Bahadur Rd, nr. PAZAMUDIR NILAYAM, R.S. Puram, Coimbatore, Tamil Nadu 641002"}, {"id": 237, "rate": "Not Answering", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9884334920", "plusCode": "2X52+M2 Coimbatore, Tamil Nadu", "address": "41-A, West Lokamanya Street, d b road ground floor Opp. Tanishq Jewellery, R.S. Puram, Coimbatore, Tamil Nadu 641002"}, {"id": 238, "rate": "Cannot receving incoming call", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Peelamedu", "phone": "7457816603", "plusCode": "2X8R+PF Coimbatore, Tamil Nadu", "address": "515, AVINASHI ROAD, OPP KFC RESTAURANT, NAVA INDIA, Peelamedu, Coimbatore, Tamil Nadu 641037"}, {"id": 239, "rate": 300.0, "pickup": "yes", "drop": "yes", "timeInDays": 3.0, "packing": "yes", "company": null, "area": "Ganapathy", "phone": "7567004601", "plusCode": "2XMH+J8 Coimbatore, Tamil Nadu", "address": "22-23, Sathy Rd, opp. aathithya medical center, moor market, Ganapathy, Coimbatore, Tamil Nadu 641006"}, {"id": 243, "rate": "Lack of infromation ", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "SIUS Logistics", "area": "Tatabad", "phone": "9842279108", "plusCode": "2XF5+X8 Coimbatore, Tamil Nadu", "address": "No 109, Tank Road, Sivananda Colony, Tatabad, Coimbatore, Tamil Nadu 641012"}, {"id": 244, "rate": "Switch off ", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "RS Puram", "phone": "6369325431", "plusCode": "2X42+VF Coimbatore, Tamil Nadu", "address": "Big bell Complex, 106/46, Diwan Bahadur Rd, R.S. Puram, Coimbatore, Tamil Nadu 641002"}, {"id": 248, "rate": "Not Answered", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "Smart and Speed Logitics", "area": "RS Puram", "phone": null, "plusCode": "2W4W+W2 Coimbatore, Tamil Nadu", "address": "1196, Thadagam Main Rd, Near Milk Company, Rangamuthu Complex, R.S. Puram, Coimbatore, Tamil Nadu 641002"}, {"id": 252, "rate": "International Service Only and has their own pickup", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "Oscar Express Worldwide", "area": "Saibaba Colony", "phone": "9087005666", "plusCode": "2WGV+83 Coimbatore, Tamil Nadu", "address": "DHL Express Center No.3,Ramalinga Nagar, 1st Cross Rd, opp. Annapoorna Hotel, Saibaba Colony, Coimbatore, Tamil Nadu 641011"}, {"id": 253, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Ram Nagar", "phone": "9626250204", "plusCode": "2X86+JF Coimbatore, Tamil Nadu", "address": "No.452, 453, Vivekananda Rd, near KMCH city center, Ram Nagar, Coimbatore, Tamil Nadu 641009"}, {"id": 254, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "RS Puram", "phone": "9843420204", "plusCode": "2X42+RC Coimbatore, Tamil Nadu", "address": "DHL Express Center No.106/15, Big Bell Complex, Ground Floor, Diwan Bahadur Rd, near Palamudhir Fruit Shop, R.S. Puram, Coimbatore, Tamil Nadu 641002"}, {"id": 258, "rate": "Contact Invalid", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "K Express", "area": "RS Puram", "phone": "7714913256", "plusCode": "2X32+85 Coimbatore, Tamil Nadu", "address": "63, Subramaniam Rd, R.S. Puram, Coimbatore, Tamil Nadu 641002"}, {"id": 262, "rate": "International Service only. Don't have their pickup and package service", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "Garuda Vega Delivery Service", "area": "BR Puram", "phone": "9133555718", "plusCode": "22G9+PV Coimbatore, Tamil Nadu", "address": "VINAYAKA TOWERS, NO:2, Sarkarayar St, near SRKP HOTEL, opp. to KONGU MEDICALS, BR Puram, Coimbatore, Tamil Nadu 641004"}, {"id": 263, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Thudiyalur", "phone": "9943699431", "plusCode": "3W9V+78 Coimbatore, Tamil Nadu", "address": "Shop No: F5, 23, Sreevatsa Square Mettupalayam Main Road, Thudiyalur, Coimbatore, Tamil Nadu 641034"}, {"id": 264, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Gandhi nagar", "phone": "7207995426", "plusCode": "XX2G+W4 Coimbatore, Tamil Nadu", "address": "Canara Bank, Pollachi Main Rd, near Krishna sweets, Gandhi Nagar, Coimbatore, Tamil Nadu 641024"}, {"id": 265, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Sowripalayam", "phone": "8129497694", "plusCode": "XXXX+5P Ramanathapuram, Tamil Nadu", "address": "103/104, Nagappan St, opposite NM Hospital, Sowripalayam Pirivu, Ram Nagar, Ramanathapuram, Coimbatore, Tamil Nadu 641045"}, {"id": 266, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Chinniyampalayam", "phone": "7075496586", "plusCode": "3337+CJ Coimbatore, Tamil Nadu", "address": "2/102 A, Irugur, Chinniyampalayam Rd, near OLD BSNL OFFICE, Chinniyampalayam, Coimbatore, Tamil Nadu 641062"}, {"id": 267, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "8712654819", "plusCode": "32H4+3V Coimbatore, Tamil Nadu", "address": "Ground floor No:138 Sathy Main road, 4, Road Junction, near signal, opposite to Poorvika Mobiles, Coimbatore, Tamil Nadu 641035"}, {"id": 268, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Ram Nagar", "phone": "8129497694", "plusCode": "XXXX+5P Ramanathapuram, Tamil Nadu", "address": "103/104, Nagappan St, opposite NM Hospital, Sowripalayam Pirivu, Ram Nagar, Ramanathapuram, Coimbatore, Tamil Nadu 641045"}, {"id": 269, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Ganapathy", "phone": "9843123444", "plusCode": "2XQJ+JP Coimbatore, Tamil Nadu", "address": "4, KRG Nagar Main St, KRG Nagar, Ganapathy, Coimbatore, Tamil Nadu 641006"}, {"id": 270, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "KK Pudur", "phone": "7075459807", "plusCode": "2WMP+H9 Coimbatore, Tamil Nadu", "address": "39/2, 3rd cross, Manjeeshwary Colony, Rajiv Nagar, Edayarpalayam, K K Pudur, Coimbatore, Tamil Nadu 641030"}, {"id": 271, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "RS Puram", "phone": "9677131717", "plusCode": "2X52+59 Coimbatore, Tamil Nadu", "address": "Arunraj Building, 93/6, Diwan Bahadur Rd, R.S. Puram, Coimbatore, Tamil Nadu 641002"}, {"id": 275, "rate": "Not Answering", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "Shree Maruti Courier Service Private Limited", "area": "Lakshmi MIlls", "phone": "9047377795", "plusCode": "2X8Q+RX Coimbatore, Tamil Nadu", "address": "near The Lakshmi Mills Company Limited, Coimbatore, Tamil Nadu 641037"}, {"id": 276, "rate": 120.0, "pickup": "yes", "drop": "yes", "timeInDays": 3.0, "packing": "no", "company": null, "area": "Neelambur", "phone": "6381827776", "plusCode": "337P+GX Coimbatore, Tamil Nadu", "address": "Serayampalayam Rd, nearby Tag Hotels, NPS Nagar, Neelambur, Coimbatore, Tamil Nadu 641407"}, {"id": 277, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Vadavalli - Thondamuthur", "phone": "8438054778", "plusCode": "2VFV+M9 Coimbatore, Tamil Nadu", "address": "Jalli thottam, Shop no 1 sri krishna building, 1B, Vadavalli-Thondamuthur Rd, Coimbatore, Tamil Nadu 641041"}, {"id": 278, "rate": 80.0, "pickup": "yes", "drop": "yes", "timeInDays": 3.0, "packing": 50.0, "company": null, "area": "RS Puram", "phone": "4224393995", "plusCode": "2X52+R5 Coimbatore, Tamil Nadu", "address": "554, Diwan Bahadur Rd, R.S. Puram, Coimbatore, Tamil Nadu 641002"}, {"id": 282, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "Parcel Service Private Limited", "area": "RS Puram", "phone": "9842254185", "plusCode": "2W6R+QP Coimbatore, Tamil Nadu", "address": "4, Thadagam Main Rd, Angammal layout, roadrs puram, Coimbatore, Tamil Nadu 641002"}, {"id": 286, "rate": "Wrong number", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "Sindhu Cargo Service", "area": "Trichy Road", "phone": "4222362920", "plusCode": "XXX5+7C Coimbatore, Tamil Nadu", "address": "740, Trichy Rd, near IDBI Bank, Old City, Coimbatore, Tamil Nadu 641001"}, {"id": 287, "rate": "Number does not exist", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Gopalapuram", "phone": "4222301920", "plusCode": null, "address": "Shop No 746 1st Floor TJS Complex Avinsihi Road Gopalapuram Coimbatore , Tamil Nadu 641018"}, {"id": 291, "rate": "International Service only. They have their own pickup service", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "Indian International Express", "area": "Saravanapatti", "phone": "7339235666", "plusCode": "2XWQ+P4 Coimbatore, Tamil Nadu", "address": "DHL EXPRESS | FedEX, International Courier Free Pickup No 1423 A3 opp. Annapoorna Hotels Ganapathy, Saravanampatti, Coimbatore, Tamil Nadu 641006"}, {"id": 292, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Ram Nagar", "phone": "7339235666", "plusCode": "2X86+JF Coimbatore, Tamil Nadu", "address": "No.452, 453, Vivekananda Rd, Peranaidu Layout, Ram Nagar, Coimbatore, Tamil Nadu 641009"}, {"id": 296, "rate": "Asking to bring the Parcel.", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "Seenu Transports Pvt Ltd", "area": "Sddhapudur", "phone": "9382279086", "plusCode": "2XCJ+5P New Siddhapudur, Tamil Nadu", "address": "Shop No.14-B, , Siddhapudur, Indira Gandhi Nagar, New Siddhapudur, Tamil Nadu 641044"}, {"id": 297, "rate": "Not Answering", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Sundrapuram", "phone": "9380279081", "plusCode": null, "address": "252 Pollachi Main road, Opposite Sumathi Villas, Sundrapuram Ram nagar, Coimbatore, Tamil Nadu , 641018"}, {"id": 298, "rate": "Not Answering", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Ram Nagar", "phone": "9380279081", "plusCode": "2X58+6M Coimbatore, Tamil Nadu", "address": "252, Pollachi Main Rd, Opposite Sumathi Villas, Sundarapuram, Ram Nagar, Coimbatore, Tamil Nadu 641018"}, {"id": 299, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "BR Puram", "phone": "4226537977", "plusCode": "22H8+7M Coimbatore, Tamil Nadu", "address": "1b, Villankurichi Rd, BR Puram, PKD Nagar, Coimbatore, Tamil Nadu 641004"}, {"id": 303, "rate": "No service to hydrabad", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "Kannur Speed Parcel service Cargo", "area": "Ram Nagar", "phone": "9043010678", "plusCode": "2X84+HV Coimbatore, Tamil Nadu", "address": "No : 23, Patel Rd, Ram Nagar, Coimbatore, Tamil Nadu 641009"}, {"id": 307, "rate": "40 - 60", "pickup": "no", "drop": "yes", "timeInDays": 1.0, "packing": "no", "company": "Wayanad Parcel Service Coimbatore", "area": "Ram Nagar", "phone": "8606103443", "plusCode": "2X65+MW Coimbatore, Tamil Nadu", "address": "Door No, 98, Patel Rd, Ram Nagar, Coimbatore, Tamil Nadu 641009"}, {"id": 311, "rate": "Not Answering ", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "NJK Tarvels and Speed Parcel", "area": "Sddhapudur", "phone": "9894663939", "plusCode": "2XFJ+H2 Coimbatore, Tamil Nadu", "address": "No.3 , Dhanalakshmi Nagar New Siddhapudhur, Coimbatore - 641012, Coimbatore, Tamil Nadu 641044"}, {"id": 315, "rate": "Only International Service ", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "Safe International Logistics", "area": "Sddhapudur", "phone": "9787411440", "plusCode": "2X9C+2M Coimbatore, Tamil Nadu", "address": "Door no. 4, Siddhapudur, RR Layout, New Siddhapudur, Coimbatore, Tamil Nadu 641044"}, {"id": 319, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "City Express Parcel Service", "area": "A.T.T colony", "phone": "4222212676", "plusCode": "2X5C+CJ Coimbatore, Tamil Nadu", "address": "City express parcel servicem 122a, a.t.t. colony, near The Hindhu, Coimbatore, Tamil Nadu 641018"}, {"id": 320, "rate": "Not Answering", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Ganapathy", "phone": "4224202488", "plusCode": "2XQH+4P Coimbatore, Tamil Nadu", "address": "197, Sathy Rd, Opposite To Anbu Pazhamudhir Nilayam, Gandhi Nagar, Ganapathy, Coimbatore, Tamil Nadu 641006"}, {"id": 321, "rate": "Number does not Exist", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Sddhapudur", "phone": "4224374218", "plusCode": "2XCJ+FF New Siddhapudur, Tamil Nadu", "address": "8, Dr Rajendra Prasad Rd, Scheme Rd, Indira Nagar, G K D Nagar, New Siddhapudur, Coimbatore, Tamil Nadu 641044"}, {"id": 325, "rate": 160.0, "pickup": "okay but nearby locations only ", "drop": "yes ", "timeInDays": 3.0, "packing": "no", "company": "Marbles Worldwide Express", "area": "Ganapathy", "phone": "8438756675", "plusCode": "2XVM+6P Coimbatore, Tamil Nadu", "address": "No.1414, FIRST FLOOR MIDDLE BUILDING, Sakthy Main Rd, Ganapathy, Coimbatore, Tamil Nadu 641006"}, {"id": 329, "rate": "Wrong contact number ", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "Carrycareruckx Domestic and international Courier Service", "area": "Ganapathy", "phone": "7339429990", "plusCode": "2XVR+CG Coimbatore, Tamil Nadu", "address": "3A-3, FCI Rd, Varadharajulu Nagar, Subhash Nagar, Ganapathy, Coimbatore, Tamil Nadu 641006"}, {"id": 333, "rate": 130.0, "pickup": "yes (100)", "drop": "yes", "timeInDays": 5.0, "packing": "yes", "company": "Fly Speed Courier", "area": null, "phone": "7639988196", "plusCode": "XWH9+32 Coimbatore, Tamil Nadu", "address": "1/146 Perur, Town Hall, Coimbatore, Tamil Nadu 641010"}, {"id": 337, "rate": "Lack of information ", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "Vel International Logistics", "area": "KR Pudur", "phone": "9597986032", "plusCode": "2WGQ+FV Coimbatore, Tamil Nadu", "address": "No 10 PV, Krishnan St, Saibaba Colony, Kuppakonam Pudur, K K Pudur, Coimbatore, Tamil Nadu 641038"}, {"id": 341, "rate": "Switch off ", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "VSP International Courier", "area": "Peelamedu", "phone": "8438089878", "plusCode": "22C2+M5 Coimbatore, Tamil Nadu", "address": "69, Pudur Main Rd, Peelamedu, Post, Coimbatore, Tamil Nadu 641004"}, {"id": 345, "rate": "Not answered ", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "NRI XpressvInter International Courier", "area": "Peelamedu", "phone": "4222988281", "plusCode": "22RF+3F Coimbatore, Tamil Nadu", "address": "61, Codissa Road, Peelamedu, Sivasakthi Nagar, Coimbatore, Tamil Nadu 641004"}, {"id": 349, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "Worldwide Parcel Service", "area": "Peelamedu", "phone": "9843220916", "plusCode": "22C9+R5 Coimbatore, Tamil Nadu", "address": "2b, Metha layout, Masakalipalayam Rd, Peelamedu, Coimbatore, Tamil Nadu 641004"}, {"id": 353, "rate": "Lack of information ", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "Dutch Associates", "area": "Peelamedu", "phone": "9791951821", "plusCode": "22C9+V5 Coimbatore, Tamil Nadu", "address": "Ground Floor, 2A, Metha layout, Masakalipalayam Rd, Peelamedu, Coimbatore, Tamil Nadu 641004"}, {"id": 357, "rate": "Not answered", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "Courier Dos Mertic and Trader", "area": "Uppilipalayam ", "phone": "9443726770", "plusCode": "2X99+FJ Coimbatore, Tamil Nadu ", "address": "39, Avinashi Road, Avanashi Road, near Annasilai, Uppilipalayam, Coimbatore, Tamil Nadu 641012"}, {"id": 361, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "ARC Parcel Service Pvt Ltd", "area": "Town Hall", "phone": "9842417800", "plusCode": "XXV4+RP Coimbatore, Tamil Nadu", "address": "428, Raja St, Town Hall, Coimbatore, Tamil Nadu 641001"}, {"id": 362, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9842414800", "plusCode": "XXW4+V7 Coimbatore, Tamil Nadu", "address": "622, Edayar St, Town Hall, Coimbatore, Tamil Nadu 641001"}, {"id": 363, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "RS Puram", "phone": "4222541600", "plusCode": "2X63+42 Coimbatore, Tamil Nadu", "address": "2nd floor, Vijay Enclave Abhilasha Building, 79/2, Sir Shanmugam Rd, R.S. Puram, Coimbatore, Tamil Nadu 641002"}, {"id": 364, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9842254185", "plusCode": "2W5X+H3 Coimbatore, Tamil Nadu", "address": "#10, Azad Main Road, R S Puram,, Coimbatore, Tamil Nadu 641002"}, {"id": 365, "rate": "Number does not exist ", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9842449600", "plusCode": "2X35+96 Coimbatore, Tamil Nadu", "address": "Thiyagaraya New Street, Sukrawar Pettai, R.S. Puram, Coimbatore, Tamil Nadu 641001"}, {"id": 366, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Gandhipuram", "phone": "4222529700", "plusCode": "2X97+XR Gandhipuram, Tamil Nadu", "address": "172, Dr Rajendra Prasad Rd, Gandhipuram, Coimbatore, Tamil Nadu 641012"}, {"id": 367, "rate": 90.0, "pickup": "yes", "drop": "yes", "timeInDays": "5 - 6", "packing": "yes", "company": null, "area": "Tatabad", "phone": "9842159600", "plusCode": "2X93+8F Coimbatore, Tamil Nadu", "address": "T.P Road, 40 D, opp. VINAYAGA CINEMAS ANNUR, Tatabad, Coimbatore, Tamil Nadu 641043"}, {"id": 368, "rate": "Number does not exist ", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Siddhapudur", "phone": "9842413700", "plusCode": "2XCF+FQ Coimbatore, Tamil Nadu", "address": "#162, nuv lay out, GP Mahal, New Siddhapudur, Coimbatore, Tamil Nadu 641044"}, {"id": 369, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Ram nagar", "phone": "9842253800", "plusCode": "2X66+2Q Coimbatore, Tamil Nadu", "address": "43, 43, Ansari St, Venkataswamy Layout, Kattoor, Ram Nagar, Coimbatore, Tamil Nadu 641009"}, {"id": 370, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "4224276364", "plusCode": "2X65+RQ Coimbatore, Tamil Nadu", "address": "421-A/1, Patel Rd, Ram Nagar, Coimbatore, Tamil Nadu 641009"}, {"id": 371, "rate": "Number does not exist ", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Koundapalayam", "phone": "7373763640", "plusCode": "2XR3+9V Coimbatore, Tamil Nadu", "address": "no.1, suguna nagar 1st cross, corporation high school,, Nallampalayam Rd, Koundampalayam, Coimbatore, Tamil Nadu 641027"}, {"id": 372, "rate": 50.0, "pickup": "no", "drop": "yes ", "timeInDays": 7.0, "packing": "no", "company": null, "area": "Saibaba Kovil", "phone": "4222529700", "plusCode": "2XJ2+Q9 Coimbatore, Tamil Nadu", "address": "170, sri avanasilingam HSC, Mettupalayam Rd, near ctc bus depos, Saibaba Koil, Coimbatore, Tamil Nadu 641043"}, {"id": 373, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Ramanathapuram", "phone": "9865243100", "plusCode": "XXXP+7W Ramanathapuram, Tamil Nadu", "address": "MR. R. SELVARJ, OLD NO: 1668, NEW, 1050, Trichy Rd, Ramanathapuram, Coimbatore, Tamil Nadu 641045"}, {"id": 374, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Velandipalayam", "phone": "7373058700", "plusCode": "2WHM+F3 Velandipalayam, Tamil Nadu", "address": "#139/246, Manayadi Joshier Street, Thadagam Main Rd, Velandipalayam, Tamil Nadu 641025"}, {"id": 375, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Palani Gounder Colony", "phone": "9842237400", "plusCode": null, "address": "#1/17, Palani Gounder Colony, NGGo Colony,, Coimbatore, Tamil Nadu 641004"}, {"id": 376, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Kuniyamuthur", "phone": "9842252682", "plusCode": "XX63+HV Kuniyamuthur, Tamil Nadu", "address": "Shop No. 8/160, Palakkad - Coimbatore Rd, Kuniyamuthur CKM, Kuniyamuthur, Tamil Nadu 641008"}, {"id": 377, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Viswanathapuram", "phone": "4222529700", "plusCode": "3WFR+J5 Coimbatore, Tamil Nadu", "address": "#C102, Santham Apartment, Viswanathapuram, Coimbatore, Tamil Nadu 641034"}, {"id": 378, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Singanallur", "phone": "4222671811", "plusCode": "W2C3+HV Coimbatore, Tamil Nadu", "address": "24,Jay sri nagar, near housing unit, Singanallur, Coimbatore, Tamil Nadu 641050"}, {"id": 379, "rate": "Not Answered ", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9842994100", "plusCode": "222P+H4 Singanallur, Tamil Nadu", "address": "Shop No. 410, Trichy Rd, Agraharam, Singanallur, Tamil Nadu 641005"}, {"id": 380, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Vadavalli", "phone": "9894541640", "plusCode": "2VFX+V6 Coimbatore, Tamil Nadu", "address": "#17, Club House Main Road, Thoppil Nagar, Vadavalli, Coimbatore, Tamil Nadu 641041"}, {"id": 381, "rate": "No service to our huydrabad office address ", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Thudiyalur", "phone": "9976927400", "plusCode": "3WWX+6V Coimbatore, Tamil Nadu", "address": "#13, Thirumurugan Nagar, 2nd Street, Thudiyalur, Coimbatore, Tamil Nadu 641022"}, {"id": 382, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9842797200", "plusCode": "WXMJ+84 Coimbatore, Tamil Nadu", "address": "No 504/2B, New Seerapalayam Village, Pollachi Main Road, Eachinari, Coimbatore, Tamil Nadu 641021"}, {"id": 383, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Malumachampatti", "phone": "9965344100", "plusCode": "VXWX+CW Coimbatore, Tamil Nadu", "address": "#SF 351, Hindustan Eng College, Coimbatore Pollachi, Main Road,, Malumachampatti, Coimbatore, Tamil Nadu 641050"}, {"id": 384, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Civil Aerodrome", "phone": "4222529700", "plusCode": "333R+29 Coimbatore, Tamil Nadu", "address": "#7/5, Kasthur Nagar, Thottipalayalm Privu Civil, Aeroduram, Coimbatore, Tamil Nadu 641062"}, {"id": 385, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Kurichi new town", "phone": "4222529700", "plusCode": "WXWG+R4 Coimbatore, Tamil Nadu", "address": "No.#237/C,Pollachi Main Road,Industrial Estate Post,Kurichi New town, Coimbatore, Tamil Nadu 641021"}, {"id": 386, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "GN Mills", "phone": "4222643397", "plusCode": "3W5V+VV Coimbatore, Tamil Nadu", "address": "7/51 a, VCP layout, Vellakinar pirivu, Mettupalayam Road, GN mills po, Coimbatore, Tamil Nadu 641029"}, {"id": 387, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Srinagar - Kanyakumari highway", "phone": "4222611940", "plusCode": "WX8P+C4 Coimbatore, Tamil Nadu", "address": "Rangasamykonar Nagar, Salem, Srinagar - Kanyakumari Hwy, Coimbatore, Tamil Nadu 641021"}, {"id": 388, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Salem - Kochin Highway", "phone": "4272477360", "plusCode": "X2CP+9X Vellalore, Tamil Nadu", "address": "#3/400,Sankagiri main road, Salem -kochin high way, salem, Tamil Nadu 641016"}, {"id": 389, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Madukkarai", "phone": "4222529700", "plusCode": "WX73+F4 Madukkarai, Tamil Nadu", "address": "#2/30, Bodi Palayam post, Madukkarai Via, Coimbatore, Madukkarai, Tamil Nadu 641105"}, {"id": 394, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "KPN Parcel Service", "area": "Gandhipuram", "phone": "4274222222", "plusCode": "2X99+4H Gandhipuram, Tamil Nadu", "address": "Ground Floor, Sow Ma Complex, Sathy Rd, Cross Cut, Gandhipuram, Coimbatore, Tamil Nadu 641012"}, {"id": 395, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Ram Nagar", "phone": "4274222222", "plusCode": null, "address": "2X68+CVM, Sarojini St, Ram Nagar, Coimbatore, Tamil Nadu 641018"}, {"id": 396, "rate": "No Service to Hydrabad", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Eachinari", "phone": "9894366972", "plusCode": "WXHJ+W6 Eachanari, Tamil Nadu", "address": "5, 16/1, Pollachi Main Rd, SIDCO Private Industrial Estate, Eachanari, Coimbatore, Tamil Nadu 641021"}, {"id": 397, "rate": "No Service to Hydrabad", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Vadavalli", "phone": "9489977071", "plusCode": "2WH2+8C Coimbatore, Tamil Nadu", "address": "36, Marudhamalai Rd, Vadavalli, Coimbatore, Tamil Nadu 641041"}, {"id": 398, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Singanallur", "phone": "4274222222", "plusCode": "X2XG+RH Singanallur, Tamil Nadu", "address": "545, Kongu Complex, Trichy Rd, Vasanth Nagar, Singanallur, Tamil Nadu 641005"}, {"id": 399, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Pappanaickenpalayam", "phone": "9698403777", "plusCode": "2XCP+8M Pappanaickenpalayam, Tamil Nadu", "address": "602, Pappanaickenpalayam, Tamil Nadu 641037"}, {"id": 400, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Avinashi Road", "phone": "4274222222", "plusCode": "22G9+JV Coimbatore, Tamil Nadu", "address": "1625, Vinayaka Complex, Avinashi Road, near Poorviga Mobiles, Coimbatore, Tamil Nadu 641004"}, {"id": 401, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Omni Bus stand", "phone": "4274222222", "plusCode": null, "address": "WXXG+XJQ, Omni Bus Stand, Sathy Main Road, Pollachi Main Road, Coimbatore, Tamil Nadu 641021"}, {"id": 402, "rate": "Closed ", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Illango nagar", "phone": "4222522203", "plusCode": null, "address": "2XGG+MCW, Sathy Rd, Kamadhenu Nagar, Avarampalayam, Illango Nagar, Coimbatore, Tamil Nadu 641006"}, {"id": 406, "rate": 600.0, "pickup": "yes", "drop": "yes", "timeInDays": 4.0, "packing": "yes ", "company": "ABT Parcel Service", "area": "RS Puram", "phone": "9597080359", "plusCode": "2W7W+77 Coimbatore, Tamil Nadu", "address": "T.V.SWAMY ROAD - WEST, NO.10/3,PUNNIAKODI STREET, behind KAMATCHI AMMAN KOIL STREET, R.S. Puram, Coimbatore, Tamil Nadu 641002"}, {"id": 407, "rate": "Not Answered", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "4224333666", "plusCode": "2X35+45 Coimbatore, Tamil Nadu", "address": "Lingappa Chetty St, Sukrawar Pettai, R.S. Puram, Coimbatore, Tamil Nadu 641001"}, {"id": 408, "rate": "Not Answered\t\t\t\t", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Ram nagar", "phone": "18004252675", "plusCode": "2X68+VC Coimbatore, Tamil Nadu", "address": "#10/11, Kalingarayan St, Ram Nagar, Coimbatore, Tamil Nadu 641009"}, {"id": 409, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "9944286765", "plusCode": "2X57+G8 Coimbatore, Tamil Nadu", "address": "19, Dafethar St, Kattoor Main, Ram Nagar, Coimbatore, Tamil Nadu 641009"}, {"id": 410, "rate": "Not Answered\t\t\t\t", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Saibaba Colony", "phone": "4222445091", "plusCode": "2WFX+73 Coimbatore, Tamil Nadu", "address": "404, Alagesan Rd, Nesavaalar Colony, Saibaba Colony, Coimbatore, Tamil Nadu 641011"}, {"id": 411, "rate": "Closed ", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Siddhapudur", "phone": "9994954300", "plusCode": "2XFM+H2 New Siddhapudur, Tamil Nadu", "address": "MG Rd, Balasundaram Layout, Avarampalayam, New Siddhapudur, Coimbatore, Tamil Nadu 641006"}, {"id": 412, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Race Course", "phone": "4224333666", "plusCode": "2X3H+MR Coimbatore, Tamil Nadu", "address": "180, Race Course, Coimbatore, Tamil Nadu 641018"}, {"id": 413, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Venkittapuram", "phone": "9597871511", "plusCode": "2WGM+MH Coimbatore, Tamil Nadu", "address": "216-156, Thadagam Main Rd, Bhuvaneshwari Nagar, Venkitta Puram, Coimbatore, Tamil Nadu 641025"}, {"id": 414, "rate": "EX Employee Number ", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Kuniyamuthur", "phone": "9363317333", "plusCode": "XX23+2G Kuniyamuthur, Tamil Nadu", "address": "Shop No.461, Palakkad - Coimbatore Rd, Edayarpalayam Pirivu, Kumuram Garden, Kuniyamuthur, Tamil Nadu 641008"}, {"id": 415, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Sundrapuram", "phone": "9865831258", "plusCode": "WXXG+QW Kurichi, Tamil Nadu", "address": "331D, Pollachi Main Rd, Sundarapuram, LIC Colony, Kurichi, Podanur, Tamil Nadu 641023"}, {"id": 416, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Kovaipudur", "phone": "8925901101", "plusCode": "WXP2+C7 Coimbatore, Tamil Nadu", "address": "Palakkad - Coimbatore Rd, Gokulam Colony, PIRIVUE, Coimbatore, Kovaipudur, Tamil Nadu 641042"}, {"id": 417, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Pappanaickenpalayam", "phone": "9843133773", "plusCode": "4WXW+Q2 Periyanaickenpalayam, Tamil Nadu", "address": "#72/5, LMW Rd, Near Lalila Plastic, Bharathi Nagar, Periyanaickenpalayam, Tamil Nadu 641020"}, {"id": 418, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": null, "phone": "18004252675", "plusCode": "2X7R+24 Coimbatore, Tamil Nadu", "address": "116/69,AMMANKULAM ROAD LAKSHMI MILLS BACK SIDE PAPPANAICKENPALAYAM, AMMANKULAM, Coimbatore, Tamil Nadu 641037"}, {"id": 419, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Velandipalayam", "phone": "18004252675", "plusCode": "2WHM+86 Velandipalayam, Tamil Nadu", "address": "NEW NO 145,(OLD, 261, Ramasamy Naidu St, Saibaba Colony, Velandipalayam, Tamil Nadu 641025"}, {"id": 420, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Tatabad", "phone": "9842159600", "plusCode": "2X93+8F Coimbatore, Tamil Nadu", "address": "T.P Road, 40 D, opp. VINAYAGA CINEMAS ANNUR, Tatabad, Coimbatore, Tamil Nadu 641043"}, {"id": 421, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Sulur", "phone": "9150278761", "plusCode": "24C8+WF Sulur, Tamil Nadu", "address": "SENGALIAPPA NAGAR, Ranganathapuram, Kannampalayam, Sulur, Tamil Nadu 641402"}, {"id": 422, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Gandhipuram", "phone": "9600872350", "plusCode": "2X84+7P Gandhipuram, Tamil Nadu", "address": "641, Cross Cut Rd, Eswaran Layout, Ranganathapuram, Gandhipuram, Coimbatore, Tamil Nadu 641012"}, {"id": 426, "rate": "1650 for 6 to 7 kg and every service included", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "Elbex Courier Pvt Ltd", "area": "Siddhapudur", "phone": "9585331009", "plusCode": "2X9H+93 New Siddhapudur, Tamil Nadu", "address": "258, Avaram Palayam Rd, Siddhapudur, Illango Nagar, New Siddhapudur, Coimbatore, Tamil Nadu 641044"}, {"id": 430, "rate": "Not Answered", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "A1 travels and speed parcel service ", "area": "Siddhapudur ", "phone": "9366102552", "plusCode": "2XCJ+C3 Coimbatore, Tamil Nadu", "address": "1, siddhapudur, Dhanalakshmi Nagar, Coimbatore,Tamil Nadu 641044"}, {"id": 431, "rate": "Asking to bring the parcel to the office ", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Ram Nagar", "phone": "9751112552", "plusCode": "2X57+PW Coimbatore, Tamil Nadu", "address": "313, Ranga Konar St, Kattor, Ram Nagar, Coimbatore, Tamil Nadu 641009"}, {"id": 432, "rate": "Wrong number", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Town Hall ", "phone": "9087732552", "plusCode": null, "address": "XXW3+9J4, Thomas St, Kundapur, Town Hall, Coimbatore, Tamil Nadu 641001"}, {"id": 433, "rate": "Not Answered", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Kurichi", "phone": "9787442552", "plusCode": "WXWG+66 Kurichi, Tamil Nadu", "address": "Site No.59, Bright Industrial Estate, 165, 166/A, opp. Times of India), 641021, SIDCO Private Industrial Estate, Kurichi, Tamil Nadu 641021"}, {"id": 434, "rate": "Not Answered", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Avarampalayam", "phone": "9344162552", "plusCode": null, "address": "2XGH+HJ7, Dharani Nagar, Kamadhenu Nagar, Avarampalayam, Illango Nagar, Coimbatore, Tamil Nadu 641006"}, {"id": 438, "rate": "Lack of information ", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "Sri Bharath Travels and Parcel service", "area": "Siddhapudur", "phone": "9442620191", "plusCode": "2XCH+HH New Siddhapudur, Tamil Nadu", "address": "Shop No. 71, Balaji Nagar, New Siddhapudur, Coimbatore, Tamil Nadu 641044"}, {"id": 442, "rate": "Number is out of service ", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "Sam travels and parcel service", "area": "Pappanaickenpalayam", "phone": "7845804799", "plusCode": null, "address": "No 60 E Karupakkal thottam, Nava India Rd, Pappanackenpalayam, Coimbatore, Tamil Nadu 641037"}, {"id": 446, "rate": "Wrong Number ", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "KNT Logistics and Travels ", "area": "Ram Nagar", "phone": "7548849444", "plusCode": "2X57+F7 Coimbatore, Tamil Nadu", "address": "133, Dr, Raja Rathinam St, Kattoor Main, Kattoor, Ram Nagar, Coimbatore, Tamil Nadu 641009"}, {"id": 450, "rate": "Number is out of service ", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "Rathimeena travels and speed parcel service ", "area": "GB theater", "phone": "9566004926", "plusCode": null, "address": "2XFC+2H5 Omni Bus Stand Complex, Dr Radhakrishanan road, GB Theater, Coimbatore, Tamil Nadu 641012"}, {"id": 454, "rate": "No contact number found ", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "Nova Travels and Speed Parcel Service ", "area": "Pudur", "phone": null, "plusCode": "2X9M+XR Coimbatore, Tamil Nadu", "address": "319, PN Palayam Rd, New Chitrambalam Layout, Ponni Nagar, Pudur, Coimbatore, Tamil Nadu 641037"}, {"id": 455, "rate": null, "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Sukrawarpet", "phone": null, "plusCode": "XXW4+VM Coimbatore, Tamil Nadu", "address": "747, Edayar St, Sukrawarpet, Old City, Coimbatore, Tamil Nadu 641001"}, {"id": 459, "rate": "Out of service ", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "VRL Logistics ", "area": "Ram Nagar", "phone": "4222237779", "plusCode": "2X75+78 Coimbatore, Tamil Nadu", "address": "398, Patel Rd, Main, Kattoor, Ram Nagar, Coimbatore, Tamil Nadu 641009"}, {"id": 460, "rate": "Not Answered", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Selvapuram South", "phone": "9344058201", "plusCode": "XXQ2+W7 Coimbatore, Tamil Nadu", "address": "Darshan Complex, 1/3 Chetty Veedhi, Deventhira St, Savithiri Nagar, Ashoknagar, Naicker, Selvapuram South, Coimbatore, Tamil Nadu 641001"}, {"id": 461, "rate": 9.0, "pickup": "no", "drop": "no", "timeInDays": 5.0, "packing": "no", "company": null, "area": "Singanallur ", "phone": "9344001679", "plusCode": "X2WG+P7 Coimbatore, Tamil Nadu", "address": "Survey No 3/5, Balaji Nagar, near Elgi Equipments Ltd, Vasanth Nagar, Singanallur, Coimbatore, Tamil Nadu 641005"}, {"id": 465, "rate": "Out of service ", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": "Rivigo Logistics ", "area": "Ramanathapuram ", "phone": "8056610452", "plusCode": null, "address": "XXRG+GX6, Shanmuga Nagar, Ramanathapuram, Coimbatore, Tamil Nadu 641045"}, {"id": 466, "rate": "No contact number", "pickup": null, "drop": null, "timeInDays": null, "packing": null, "company": null, "area": "Kamachipuram ", "phone": null, "plusCode": null, "address": "237F+M87, NH 544, Kamachipuram, Coimbatore, Tamil Nadu 641103"}] \ No newline at end of file diff --git a/src/pages/team/TeamUsers.jsx b/src/pages/team/TeamUsers.jsx index 1f23716..749c46a 100644 --- a/src/pages/team/TeamUsers.jsx +++ b/src/pages/team/TeamUsers.jsx @@ -24,24 +24,11 @@ import StatusChip from '@/components/StatusChip'; import EmptyState from '@/components/EmptyState'; import UserAvatar from '@/components/UserAvatar'; import TabLabelCount from '@/components/TabLabelCount'; -import { fetchPoints, deletePoint, COLLECTIONS } from '@/utils/qdrant'; +import { fetchUsers, deleteUser } from '@/utils/apiClient'; +import { toUser } from '@/utils/mappers'; +import { titleCase } from '@/utils/format'; import UserFormDialog from './UserFormDialog'; -// Map a raw Qdrant point from doormile_auth to a flat team-user row. -function toUser(point) { - const p = point.payload || {}; - return { - id: point.id, - name: p.name || '—', - email: p.email || '', - phone: p.phone || '', - role: p.role || 'unknown', - pin: p.pin || '' - }; -} - -const titleCase = (s) => String(s || '').replace(/[_-]+/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()); - // Per-role accent colour + icon, used for the avatar badge and role chip. const ROLE_META = { admin: { color: 'primary', icon: AdminPanelSettingsOutlinedIcon }, @@ -111,7 +98,7 @@ export default function TeamUsers() { const load = () => { setLoading(true); setError(null); - fetchPoints(COLLECTIONS.teamUsers) + fetchUsers() .then((points) => setUsers(points.map(toUser))) .catch((e) => setError(e.message || 'Failed to load team users')) .finally(() => setLoading(false)); @@ -151,7 +138,7 @@ export default function TeamUsers() { const confirmDelete = async () => { setDeleting(true); try { - await deletePoint(COLLECTIONS.teamUsers, toDelete.id); + await deleteUser(toDelete.id); setToDelete(null); load(); } catch (e) { @@ -203,7 +190,7 @@ export default function TeamUsers() { {filtered.length} {filtered.length === 1 ? 'user' : 'users'} - + )} @@ -291,7 +278,7 @@ export default function TeamUsers() { Delete user? - This will permanently remove {toDelete?.name} from the doormile_auth collection. This cannot be undone. + This will permanently remove {toDelete?.name} from the PostgreSQL database. This cannot be undone. diff --git a/src/pages/team/UserFormDialog.jsx b/src/pages/team/UserFormDialog.jsx index 67bc237..3d15c8b 100644 --- a/src/pages/team/UserFormDialog.jsx +++ b/src/pages/team/UserFormDialog.jsx @@ -4,11 +4,11 @@ import { MenuItem, Alert, CircularProgress, IconButton } from '@mui/material'; import CloseIcon from '@mui/icons-material/Close'; -import { createPoint, setPayload, COLLECTIONS } from '@/utils/qdrant'; +import { createUser, updateUser } from '@/utils/apiClient'; const ROLES = ['admin', 'rep', 'manager']; -const EMPTY = { name: '', email: '', phone: '', role: 'rep', pin: '' }; +const EMPTY = { name: '', email: '', phone: '', role: 'rep', pin: '', password: '' }; const withValue = (opts, v) => (v && !opts.includes(v) ? [v, ...opts] : opts); @@ -30,22 +30,24 @@ export default function UserFormDialog({ open, mode, initial, onClose, onSaved } const handleSave = async () => { if (!form.name.trim()) { setError('Name is required.'); return; } if (!form.email.trim()) { setError('Email is required.'); return; } + if (!isEdit && !form.password.trim()) { setError('Password is required.'); return; } setSaving(true); setError(null); const payload = { - name: form.name.trim(), + first_name: form.name.trim(), email: form.email.trim(), phone: form.phone, role: form.role, - ...(form.pin ? { pin: String(form.pin) } : {}) + ...(form.password ? { password: form.password } : {}), + ...(form.pin ? { pin: String(form.pin) } : {}), }; try { if (isEdit) { - await setPayload(COLLECTIONS.teamUsers, initial.id, payload); + await updateUser(initial.id, payload); } else { - await createPoint(COLLECTIONS.teamUsers, payload); + await createUser(payload); } onSaved(); } catch (e) { @@ -73,6 +75,14 @@ export default function UserFormDialog({ open, mode, initial, onClose, onSaved } + + + diff --git a/src/pages/tenants/ClientFormDialog.jsx b/src/pages/tenants/ClientFormDialog.jsx index 7fb497c..5ab0b16 100644 --- a/src/pages/tenants/ClientFormDialog.jsx +++ b/src/pages/tenants/ClientFormDialog.jsx @@ -1,24 +1,48 @@ import { useState, useEffect } from 'react'; import { Dialog, DialogTitle, DialogContent, DialogActions, Button, Grid, TextField, - MenuItem, Box, Typography, Divider, Alert, CircularProgress, IconButton + MenuItem, Box, Typography, Divider, Alert, CircularProgress, IconButton, Autocomplete, Snackbar } from '@mui/material'; import CloseIcon from '@mui/icons-material/Close'; -import { createPoint, setPayload, COLLECTIONS } from '@/utils/qdrant'; +import MyLocationIcon from '@mui/icons-material/MyLocation'; +import { createClient, updateClient } from '@/utils/apiClient'; const BUSINESS_TYPES = ['retail', 'wholesale', 'manufacturer', 'distributor', 'services', 'ecommerce', 'other']; const STATUSES = ['newClient', 'contacted', 'onboarded', 'lost']; -const FREQUENCIES = ['Daily', 'Weekly', 'Fortnightly', 'Monthly', 'Occasional']; +const FREQUENCIES = ['Daily', 'Weekly', 'Bi-weekly', 'Monthly', 'On-demand']; const CONSENTS = ['basicOnly', 'full', 'none']; +const PROVIDERS = [ + 'Blue Dart', 'Delhivery', 'DTDC', 'India Post / Speed Post', 'The Professional Couriers', 'XpressBees', 'Ecom Express', 'Shadowfax', + 'Safexpress', 'VRL Logistics', 'TCI (Transport Corporation of India)', 'Om Logistics', 'Best Roadways', + 'MSS (Mettur Super Services)', 'ABT Travels & Logistics', 'Navata Road Transport', 'KRS (Kerala Roadways)', 'Parveen Travels / Parveen Express', 'SRM Transports', 'KPN Travels & KPN Speed Parcel', 'SRS Travels', + 'Hindusthan Travels', 'City Travels', 'Essaar Travels', 'No. 1 Air Travels', 'A1 Travels', 'Krish Travels', 'Hebron Transports', 'Horma Travels', 'Vivegam Travels', 'PSS Transport', 'SRT (Renugambal Travels)', 'Thamarai Bus Transports', 'Rathimeena Travels', 'Ganesh Travels', 'John Kennedy Bus Service', 'Arun Travel', 'Saaji Meera Roadways', 'ARC Parcel Service', 'Chakra Travels & Parcel Service', 'MVA Parcel And Bus Service', + 'Shrinath Travels & Cargo', 'Hans Travels', 'Zingbus', 'Kalpana Travels / City Land Travels', 'Trackon Couriers', 'North India Transways', 'RSRTC Cargo', 'UPSRTC Cargo' +]; + +const CITIES = [ + 'Chennai', 'Coimbatore', 'Madurai', 'Tiruchirappalli', 'Salem', 'Tuticorin', 'Tirupur', 'Erode', 'Vellore', 'Tirunelveli', 'Thanjavur', 'Dindigul', 'Hosur', 'Nagercoil', 'Karur', 'Namakkal', 'Kanchipuram', 'Cuddalore', 'Thoothukudi', + 'Bengaluru', 'Mysuru', 'Mangaluru', 'Hubli', 'Belagavi', 'Kalaburagi', 'Davangere', 'Ballari', + 'Kochi', 'Thiruvananthapuram', 'Kozhikode', 'Kannur', 'Thrissur', 'Kollam', + 'Hyderabad', 'Warangal', 'Visakhapatnam', 'Vijayawada', 'Tirupati', 'Guntur', 'Rajahmundry', 'Nellore', + 'Mumbai', 'Pune', 'Nagpur', 'Nashik', 'Aurangabad', 'Kolhapur', 'Solapur', + 'New Delhi', 'Gurugram', 'Noida', 'Faridabad', 'Chandigarh', 'Amritsar', 'Ludhiana', 'Jalandhar', + 'Ahmedabad', 'Surat', 'Vadodara', 'Rajkot', 'Bhavnagar', 'Jamnagar', + 'Jaipur', 'Jodhpur', 'Udaipur', 'Kota', 'Bikaner', + 'Lucknow', 'Kanpur', 'Varanasi', 'Agra', 'Prayagraj', 'Gorakhpur', 'Bhopal', 'Indore', 'Gwalior', 'Jabalpur', 'Raipur', + 'Kolkata', 'Siliguri', 'Durgapur', 'Patna', 'Gaya', 'Ranchi', 'Jamshedpur', 'Bhubaneswar', 'Cuttack', 'Guwahati', 'Dibrugarh', 'Agartala', 'Imphal', 'Shillong', 'Aizawl', 'Dimapur', + 'Dehradun', 'Haridwar', 'Shimla', 'Srinagar', 'Jammu', 'Leh', 'Panaji', 'Puducherry', 'Port Blair' +]; + const EMPTY = { - name: '', phone: '', city: '', businessState: '', businessType: 'retail', status: 'newClient', - frequency: 'Daily', parcelVolume: 0, activeContracts: 0, provider: '', efficiency: '', + name: '', email: '', password: '', phone: '', city: '', businessState: '', + businessType: 'retail', status: 'newClient', frequency: 'Daily', + parcelVolume: 0, activeContracts: 0, provider: '', efficiency: '', logisticsSegment: '', transitFrom: '', transitTo: '', neighbourhood: '', - surveyAddress: '', surveyLat: '', surveyLng: '', dataConsent: 'basicOnly', notes: '' + surveyAddress: '', surveyLat: '', surveyLng: '', dataConsent: 'full', notes: '', pincode: '' }; -// Ensure a select always has its current value among the options. +const formatOption = (s) => typeof s === 'string' ? s.charAt(0).toUpperCase() + s.slice(1).replace(/([A-Z])/g, ' $1').trim() : s; const withValue = (opts, v) => (v && !opts.includes(v) ? [v, ...opts] : opts); export default function ClientFormDialog({ open, mode, initial, onClose, onSaved }) { @@ -26,6 +50,47 @@ export default function ClientFormDialog({ open, mode, initial, onClose, onSaved const [form, setForm] = useState(EMPTY); const [saving, setSaving] = useState(false); const [error, setError] = useState(null); + const [gettingLocation, setGettingLocation] = useState(false); + + const handleGPS = () => { + if (!navigator.geolocation) { + alert("Geolocation is not supported by your browser"); + return; + } + setGettingLocation(true); + navigator.geolocation.getCurrentPosition(async (pos) => { + const lat = pos.coords.latitude; + const lng = pos.coords.longitude; + setForm((f) => ({ ...f, surveyLat: lat, surveyLng: lng })); + + try { + const res = await fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}`); + const data = await res.json(); + if (data && data.address) { + const address = data.address; + const city = address.city || address.town || address.village || address.county || ''; + const state = address.state || ''; + const pincode = address.postcode || ''; + const display = data.display_name || ''; + + setForm((f) => ({ + ...f, + city: city || f.city, + businessState: state || f.businessState, + pincode: pincode || f.pincode, + surveyAddress: display || f.surveyAddress + })); + } + } catch (e) { + console.error("Reverse geocoding failed", e); + } finally { + setGettingLocation(false); + } + }, (err) => { + alert("Unable to retrieve your location"); + setGettingLocation(false); + }); + }; useEffect(() => { if (open) { @@ -45,7 +110,7 @@ export default function ClientFormDialog({ open, mode, initial, onClose, onSaved const lng = form.surveyLng === '' ? undefined : Number(form.surveyLng); const payload = { - name: form.name.trim(), + first_name: form.name.trim(), phone: form.phone, city: form.city, businessState: form.businessState, @@ -60,25 +125,22 @@ export default function ClientFormDialog({ open, mode, initial, onClose, onSaved transitFrom: form.transitFrom, transitTo: form.transitTo, neighbourhood: form.neighbourhood, + pincode: form.pincode, surveyAddress: form.surveyAddress, - surveyZone: form.neighbourhood, dataConsent: form.dataConsent, notes: form.notes, lastUpdated: new Date().toISOString().slice(0, 10), - ...(lat != null ? { surveyLat: lat } : {}), - ...(lng != null ? { surveyLng: lng } : {}), - ...(lat != null && lng != null ? { surveyGeo: { lat, lon: lng } } : {}) + registration_source: 'web', + ...(form.email.trim() ? { email: form.email.trim() } : {}), + ...(lat != null ? { survey_lat: lat } : {}), + ...(lng != null ? { survey_long: lng } : {}), }; try { if (isEdit) { - await setPayload(COLLECTIONS.clients, initial.id, payload); + await updateClient(initial.id, payload); } else { - await createPoint(COLLECTIONS.clients, { - ...payload, - clientId: `client_${Date.now()}`, - surveySubmitted: false - }); + await createClient({ ...payload, ...(form.password ? { password: form.password } : {}) }); } onSaved(); } catch (e) { @@ -89,36 +151,59 @@ export default function ClientFormDialog({ open, mode, initial, onClose, onSaved }; return ( - - - {isEdit ? 'Edit Client' : 'Add Client'} - - - - {error && {error}} + <> + setError(null)} + anchorOrigin={{ vertical: 'top', horizontal: 'right' }} + sx={{ zIndex: 9999 }} + > + setError(null)} severity="error" variant="filled" sx={{ width: '100%', boxShadow: 3 }}> + {error} + + - Business + + + {isEdit ? 'Edit Client' : 'Add Client'} + + + + Business + + {!isEdit && ( + + )} - {withValue(BUSINESS_TYPES, form.businessType).map((o) => {o})} + {withValue(BUSINESS_TYPES, form.businessType).map((o) => {formatOption(o)})} - {withValue(STATUSES, form.status).map((o) => {o})} + {withValue(STATUSES, form.status).map((o) => {formatOption(o)})} - {withValue(FREQUENCIES, form.frequency).map((o) => {o})} + {withValue(FREQUENCIES, form.frequency).map((o) => {formatOption(o)})} - + + setForm((f) => ({ ...f, provider: newValue || '' }))} + renderInput={(params) => } + /> + @@ -126,13 +211,50 @@ export default function ClientFormDialog({ open, mode, initial, onClose, onSaved Location & Transit - + + setForm((f) => ({ ...f, city: newValue || '' }))} + renderInput={(params) => } + /> + + - - + + setForm((f) => ({ ...f, transitFrom: newValue || '' }))} + renderInput={(params) => } + /> + + + setForm((f) => ({ ...f, transitTo: newValue || '' }))} + renderInput={(params) => } + /> + + + + @@ -141,7 +263,7 @@ export default function ClientFormDialog({ open, mode, initial, onClose, onSaved - {withValue(CONSENTS, form.dataConsent).map((o) => {o})} + {withValue(CONSENTS, form.dataConsent).map((o) => {formatOption(o)})} @@ -154,5 +276,6 @@ export default function ClientFormDialog({ open, mode, initial, onClose, onSaved + ); } diff --git a/src/pages/tenants/Tenants.jsx b/src/pages/tenants/Tenants.jsx index da703c4..197343d 100644 --- a/src/pages/tenants/Tenants.jsx +++ b/src/pages/tenants/Tenants.jsx @@ -33,62 +33,11 @@ import StatusChip from '@/components/StatusChip'; import EmptyState from '@/components/EmptyState'; import UserAvatar from '@/components/UserAvatar'; import TabLabelCount from '@/components/TabLabelCount'; -import { fetchPoints, deletePoint, COLLECTIONS } from '@/utils/qdrant'; +import { fetchClients, deleteClient } from '@/utils/apiClient'; +import { toClient } from '@/utils/mappers'; +import { titleCase } from '@/utils/format'; import ClientFormDialog from './ClientFormDialog'; -const generateLogicalId = (id) => { - const str = String(id).replace(/[^a-zA-Z0-9]/g, '').toUpperCase(); - return 'CLI-' + str.substring(0, 6).padStart(6, '0'); -}; - -// Map a raw Qdrant point from doormile_clients to a flat client row. -function toClient(point) { - const p = point.payload || {}; - - // Qdrant point.id is usually a UUID. - // We generate a clean CLI-XXXXXX format based on it. - const logicalId = generateLogicalId(point.id); - - return { - id: point.id, - logicalId, - // Force override the ugly payload client_timestamp string with the clean ID - clientId: logicalId, - name: p.name || '—', - phone: p.phone || '', - city: p.city || '', - businessState: p.businessState || '', - businessType: p.businessType || '', - status: p.status || 'unknown', - parcelVolume: p.parcelVolume ?? 0, - activeContracts: p.activeContracts ?? 0, - frequency: p.frequency || '', - provider: p.provider || '', - efficiency: p.efficiency || '', - logisticsSegment: p.logisticsSegment || '', - transitFrom: p.transitFrom || '', - transitTo: p.transitTo || '', - neighbourhood: p.neighbourhood || p.surveyZone || '', - surveyAddress: p.surveyAddress || '', - surveyLat: p.surveyLat ?? p.surveyGeo?.lat ?? '', - surveyLng: p.surveyLng ?? p.surveyGeo?.lon ?? '', - dataConsent: p.dataConsent || '', - lastUpdated: p.lastUpdated || '', - notes: p.notes || '' - }; -} - -// Humanize raw payload tokens like `basicOnly`, `newClient`, `first_mile` → "Basic Only". -const humanize = (s) => - String(s || '') - .replace(/[_-]+/g, ' ') - .replace(/([a-z\d])([A-Z])/g, '$1 $2') - .replace(/\s+/g, ' ') - .replace(/\b\w/g, (c) => c.toUpperCase()) - .trim(); - -const titleCase = humanize; - // Map categorical enum values to a semantic palette color. const consentTone = (v) => ({ full: 'success', basiconly: 'info', none: 'default' }[String(v || '').toLowerCase()] || 'default'); const efficiencyTone = (v) => { @@ -421,16 +370,28 @@ export default function Tenants() { const [toDelete, setToDelete] = useState(null); const [deleting, setDeleting] = useState(false); - const load = () => { - setLoading(true); - setError(null); - fetchPoints(COLLECTIONS.clients) + const load = (silent = false) => { + if (!silent) { + setLoading(true); + setError(null); + } + fetchClients() .then((points) => setClients(points.map(toClient))) - .catch((e) => setError(e.message || 'Failed to load clients')) - .finally(() => setLoading(false)); + .catch((e) => { + if (!silent) setError(e.message || 'Failed to load clients'); + }) + .finally(() => { + if (!silent) setLoading(false); + }); }; - useEffect(() => { load(); }, []); + useEffect(() => { + load(); + const intervalId = setInterval(() => { + load(true); // Silent poll every 10 seconds + }, 10000); + return () => clearInterval(intervalId); + }, []); const stats = useMemo(() => ({ total: clients.length, @@ -473,7 +434,7 @@ export default function Tenants() { const confirmDelete = async () => { setDeleting(true); try { - await deletePoint(COLLECTIONS.clients, toDelete.id); + await deleteClient(toDelete.id); setToDelete(null); load(); } catch (e) { diff --git a/src/utils/apiClient.js b/src/utils/apiClient.js new file mode 100644 index 0000000..8155787 --- /dev/null +++ b/src/utils/apiClient.js @@ -0,0 +1,125 @@ +const API_BASE = import.meta.env.VITE_API_BASE || 'https://api.doormile.com/api/v1'; + +const getHeaders = () => { + const token = localStorage.getItem('auth_token'); + return { + 'Content-Type': 'application/json', + ...(token ? { 'Authorization': `Bearer ${token}` } : {}) + }; +}; + +const parseJson = (res) => res.status === 204 ? {} : res.json().catch(() => ({})); + +const extractArray = (json) => (Array.isArray(json) ? json : (Array.isArray(json?.data) ? json.data : [])); + +export async function fetchClients() { + const res = await fetch(`${API_BASE}/crm/clients?limit=1000`, { headers: getHeaders() }); + if (!res.ok) throw new Error('Failed to fetch clients'); + const json = await res.json(); + return extractArray(json); +} + +export async function loginAdmin(email, password) { + const res = await fetch(`${API_BASE}/admin/login`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email, password }), + }); + if (!res.ok) { + const errorData = await res.json().catch(() => ({})); + throw new Error(errorData.error || 'Failed to login'); + } + return res.json(); +} + +export async function createClient(payload) { + const res = await fetch(`${API_BASE}/crm/clients`, { + method: 'POST', + headers: getHeaders(), + body: JSON.stringify(payload), + }); + if (!res.ok) { + const err = await res.json().catch(() => ({})); + throw new Error(err.error || err.message || 'Failed to create client'); + } + return parseJson(res); +} + +export async function updateClient(id, payload) { + const res = await fetch(`${API_BASE}/crm/clients/${id}`, { + method: 'PUT', + headers: getHeaders(), + body: JSON.stringify(payload), + }); + if (!res.ok) { + const err = await res.json().catch(() => ({})); + throw new Error(err.error || err.message || 'Failed to update client'); + } + return parseJson(res); +} + +export async function deleteClient(id) { + const res = await fetch(`${API_BASE}/crm/clients/${id}`, { + method: 'DELETE', + headers: getHeaders() + }); + if (!res.ok) throw new Error('Failed to delete client'); + return parseJson(res); +} + +export async function fetchUsers() { + const res = await fetch(`${API_BASE}/admin/users?limit=1000`, { headers: getHeaders() }); + if (!res.ok) throw new Error('Failed to fetch users'); + const json = await res.json(); + return extractArray(json); +} + +export async function createUser(payload) { + const res = await fetch(`${API_BASE}/admin/users`, { + method: 'POST', + headers: getHeaders(), + body: JSON.stringify(payload), + }); + if (!res.ok) { + const err = await res.json().catch(() => ({})); + throw new Error(err.error || err.message || 'Failed to create user'); + } + return parseJson(res); +} + +export async function updateUser(id, payload) { + const res = await fetch(`${API_BASE}/admin/users/${id}`, { + method: 'PUT', + headers: getHeaders(), + body: JSON.stringify(payload), + }); + if (!res.ok) { + const err = await res.json().catch(() => ({})); + throw new Error(err.error || err.message || 'Failed to update user'); + } + return parseJson(res); +} + +export async function deleteUser(id) { + const res = await fetch(`${API_BASE}/admin/users/${id}`, { + method: 'DELETE', + headers: getHeaders() + }); + if (!res.ok) throw new Error('Failed to delete user'); + return parseJson(res); +} + +export async function fetchDashboard() { + const res = await fetch(`${API_BASE}/admin/dashboard`, { headers: getHeaders() }); + if (!res.ok) throw new Error('Failed to fetch dashboard'); + return res.json(); +} + +export async function deleteCompetitorBranch(id) { + const res = await fetch(`${API_BASE}/admin/competitor-branches/${id}`, { + method: 'DELETE', + headers: getHeaders() + }); + if (!res.ok) throw new Error('Failed to delete survey record'); + return parseJson(res); +} diff --git a/src/utils/format.js b/src/utils/format.js index e5e76cb..b5a0ecf 100644 --- a/src/utils/format.js +++ b/src/utils/format.js @@ -1,4 +1,10 @@ -// ==============================|| FORMAT HELPERS ||============================== // +export const titleCase = (s) => + String(s || '') + .replace(/[_-]+/g, ' ') + .replace(/([a-z\d])([A-Z])/g, '$1 $2') + .replace(/\s+/g, ' ') + .replace(/\b\w/g, (c) => c.toUpperCase()) + .trim(); export const inr = (n) => '₹' + Number(n || 0).toLocaleString('en-IN', { minimumFractionDigits: 0, maximumFractionDigits: 2 }); diff --git a/src/utils/mappers.js b/src/utils/mappers.js new file mode 100644 index 0000000..1a119b9 --- /dev/null +++ b/src/utils/mappers.js @@ -0,0 +1,51 @@ +export const generateLogicalId = (id) => { + const str = String(id).replace(/[^a-zA-Z0-9]/g, '').toUpperCase(); + return 'CLI-' + str.substring(0, 6).padStart(6, '0'); +}; + +export function toClient(raw) { + const p = raw.payload ?? raw; + const id = p.id ?? raw.id; + const logicalId = generateLogicalId(id); + return { + id, + logicalId, + clientId: logicalId, + name: p.first_name ? `${p.first_name} ${p.last_name || ''}`.trim() : (p.name || '—'), + email: p.email || '', + phone: p.phone || '', + city: p.city || '', + businessState: p.businessState || '', + businessType: p.businessType || '', + status: p.status || 'unknown', + parcelVolume: Number(p.parcelVolume) || 0, + activeContracts: Number(p.activeContracts) || 0, + frequency: p.frequency || '', + provider: p.provider || '', + efficiency: p.efficiency || '', + logisticsSegment: p.logisticsSegment || '', + transitFrom: p.transitFrom || '', + transitTo: p.transitTo || '', + neighbourhood: p.neighbourhood || p.surveyZone || '', + surveyAddress: p.surveyAddress || p.address || '', + surveyLat: p.survey_lat ?? p.surveyLat ?? '', + surveyLng: p.survey_long ?? p.surveyLng ?? '', + dataConsent: p.dataConsent || '', + lastUpdated: p.lastUpdated || '', + pincode: p.pincode || p.postal_code || '', + notes: p.notes || '' + }; +} + +export function toUser(raw) { + const p = raw.payload ?? raw; + const id = p.id ?? raw.id; + return { + id, + name: p.first_name ? `${p.first_name} ${p.last_name || ''}`.trim() : (p.name || '—'), + email: p.email || '', + phone: p.phone || '', + role: p.role || 'unknown', + pin: p.pin || '' + }; +} diff --git a/src/utils/qdrant.js b/src/utils/qdrant.js deleted file mode 100644 index ae901d5..0000000 --- a/src/utils/qdrant.js +++ /dev/null @@ -1,108 +0,0 @@ -// ==============================|| QDRANT DATA LAYER ||============================== // -// Real connection to the Doormile Qdrant cluster (read + write). -// -// Requests go through the Vite dev proxy at `/qdrant` (see vite.config.js), which -// injects the api-key server-side so it never ships in the browser bundle and CORS -// is avoided. For a production build, point VITE_QDRANT_BASE at your own proxy. - -const BASE = import.meta.env.VITE_QDRANT_BASE || '/qdrant'; - -export const COLLECTIONS = { - clients: 'doormile_clients', - teamUsers: 'doormile_auth' -}; - -async function request(path, options = {}) { - const res = await fetch(`${BASE}${path}`, { - headers: { 'Content-Type': 'application/json' }, - ...options - }); - if (!res.ok) { - let detail = res.statusText; - try { - const body = await res.json(); - detail = body?.status?.error || body?.status || detail; - } catch { /* ignore non-json error bodies */ } - throw new Error(`Qdrant ${res.status}: ${detail}`); - } - return res.json(); -} - -/** - * Scroll every point of a collection (follows next_page_offset until exhausted). - * Returns an array of { id, payload } objects with the raw Qdrant payload. - */ -export async function fetchPoints(collection, { pageSize = 250, withVector = false } = {}) { - const all = []; - let offset = null; - - for (let guard = 0; guard < 1000; guard += 1) { - const data = await request(`/collections/${collection}/points/scroll`, { - method: 'POST', - body: JSON.stringify({ - limit: pageSize, - with_payload: true, - with_vector: withVector, - ...(offset != null ? { offset } : {}) - }) - }); - - const points = data?.result?.points || []; - all.push(...points); - - offset = data?.result?.next_page_offset ?? null; - if (offset == null || points.length === 0) break; - } - - return all; -} - -// Cache vector sizes per collection so we don't re-fetch the config on every write. -const _vectorSizeCache = {}; - -export async function getVectorSize(collection) { - if (_vectorSizeCache[collection] != null) return _vectorSizeCache[collection]; - const data = await request(`/collections/${collection}`); - const vectors = data?.result?.config?.params?.vectors; - // Single unnamed vector → { size, distance }. Default to 1 if absent. - const size = typeof vectors?.size === 'number' ? vectors.size : 1; - _vectorSizeCache[collection] = size; - return size; -} - -/** - * Update the payload of an existing point (merges the given keys; vectors untouched). - */ -export async function setPayload(collection, id, payload) { - return request(`/collections/${collection}/points/payload?wait=true`, { - method: 'POST', - body: JSON.stringify({ payload, points: [id] }) - }); -} - -/** - * Create a brand-new point. The collection requires a vector of a fixed size, but - * this CRM doesn't do semantic search, so we store a zero-vector of the right length. - * Returns the generated point id. - */ -export async function createPoint(collection, payload) { - const size = await getVectorSize(collection); - const id = (crypto?.randomUUID && crypto.randomUUID()) || `${Date.now()}-${Math.random().toString(36).slice(2)}`; - const vector = new Array(size).fill(0); - - await request(`/collections/${collection}/points?wait=true`, { - method: 'PUT', - body: JSON.stringify({ points: [{ id, vector, payload }] }) - }); - return id; -} - -/** - * Delete a point by id. - */ -export async function deletePoint(collection, id) { - return request(`/collections/${collection}/points/delete?wait=true`, { - method: 'POST', - body: JSON.stringify({ points: [id] }) - }); -}