Files
nearle_console/src/pages/nearle/orders/newcreateOrder.js

2514 lines
116 KiB
JavaScript

import * as React from 'react';
import { useEffect, useState, useRef, Fragment } from 'react';
import {
FormControl,
InputAdornment,
Grid,
Typography,
Stack,
Button,
TextField,
Autocomplete,
Chip,
Divider,
DialogTitle,
DialogContent,
Checkbox,
DialogActions,
CircularProgress,
IconButton,
Switch,
OutlinedInput,
FormGroup,
FormControlLabel,
Avatar,
Box
} from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
import { Empty } from 'antd';
import { FaPhoneAlt } from 'react-icons/fa';
import { GiDoorHandle } from 'react-icons/gi';
import { FaLandmarkDome } from 'react-icons/fa6';
import ClearIcon from '@mui/icons-material/Clear';
import { useNavigate } from 'react-router';
import { TbMapPinCode } from 'react-icons/tb';
import { FaLocationDot } from 'react-icons/fa6';
import axios from 'axios';
import { useTheme } from '@mui/material/styles';
import Geocode from 'react-geocode';
import Loader from 'components/Loader';
import MainCard from 'components/MainCard';
import { FaUser } from 'react-icons/fa6';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import Dialog from '@mui/material/Dialog';
import dayjs from 'dayjs';
import { enqueueSnackbar } from 'notistack';
var utc = require('dayjs/plugin/utc');
dayjs.extend(utc);
import { ArrowDownOutlined, ArrowUpOutlined, SearchOutlined } from '@ant-design/icons';
import MyLocationIcon from '@mui/icons-material/MyLocation';
import HighlightOffIcon from '@mui/icons-material/HighlightOff';
import { OpenToast } from 'components/third-party/OpenToast';
import TitleCard from 'components/nearle_components/TitleCard';
import AccessTimeIcon from '@mui/icons-material/AccessTime';
import { TimePicker } from '@mui/x-date-pickers';
import ArrowOutwardIcon from '@mui/icons-material/ArrowOutward';
import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward';
import LocationOnOutlinedIcon from '@mui/icons-material/LocationOnOutlined';
import { MapContainer, TileLayer, Marker, Polyline, useMap } from 'react-leaflet';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
const pickupIcon = typeof window !== 'undefined' ? new L.Icon({
iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-blue.png',
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowSize: [41, 41]
}) : null;
const dropoffIcon = typeof window !== 'undefined' ? new L.Icon({
iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-red.png',
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowSize: [41, 41]
}) : null;
const MapBoundsController = ({ startPoint, endPoint }) => {
const map = useMap();
useEffect(() => {
const points = [];
const pLat = parseFloat(startPoint?.latitude);
const pLng = parseFloat(startPoint?.longitude);
const dLat = parseFloat(endPoint?.latitude);
const dLng = parseFloat(endPoint?.longitude);
if (!isNaN(pLat) && !isNaN(pLng) && pLat !== 0 && pLng !== 0) {
points.push([pLat, pLng]);
}
if (!isNaN(dLat) && !isNaN(dLng) && dLat !== 0 && dLng !== 0) {
points.push([dLat, dLng]);
}
if (points.length === 1) {
map.setView(points[0], 14);
} else if (points.length === 2) {
map.fitBounds(points, { padding: [50, 50] });
}
}, [startPoint?.latitude, startPoint?.longitude, endPoint?.latitude, endPoint?.longitude, map]);
return null;
};
const OrderMap = ({ startPoint, endPoint, appLocaLat, appLocaLng }) => {
const defaultCenter = [
parseFloat(appLocaLat) || 11.0168,
parseFloat(appLocaLng) || 76.9558
];
const hasPick = startPoint?.latitude && startPoint?.longitude && parseFloat(startPoint.latitude) !== 0;
const hasDrop = endPoint?.latitude && endPoint?.longitude && parseFloat(endPoint.latitude) !== 0;
const pickCoords = hasPick ? [parseFloat(startPoint.latitude), parseFloat(startPoint.longitude)] : null;
const dropCoords = hasDrop ? [parseFloat(endPoint.latitude), parseFloat(endPoint.longitude)] : null;
// ---- Animation state (mirrors Dispatch.js pattern) ----
const [osrmRoute, setOsrmRoute] = useState(null); // full road path [[lat,lng],...]
const [animatedSegments, setAnimatedSegments] = useState([]); // drawn pair-by-pair
const [isAnimating, setIsAnimating] = useState(false);
const isAnimatingRef = useRef(false);
const timeoutsRef = useRef([]);
// Fetch OSRM route whenever both points are available
useEffect(() => {
if (!hasPick || !hasDrop) {
setOsrmRoute(null);
setAnimatedSegments([]);
setIsAnimating(false);
return;
}
const url = `https://router.project-osrm.org/route/v1/driving/${pickCoords[1]},${pickCoords[0]};${dropCoords[1]},${dropCoords[0]}?overview=full&geometries=geojson`;
fetch(url)
.then((r) => r.json())
.then((data) => {
const coords = data?.routes?.[0]?.geometry?.coordinates;
if (coords && coords.length >= 2) {
// OSRM returns [lng, lat], Leaflet needs [lat, lng]
setOsrmRoute(coords.map(([lng, lat]) => [lat, lng]));
} else {
// Fallback to straight line
setOsrmRoute([pickCoords, dropCoords]);
}
})
.catch(() => {
setOsrmRoute([pickCoords, dropCoords]);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [startPoint?.latitude, startPoint?.longitude, endPoint?.latitude, endPoint?.longitude]);
// Reset animation when route changes
useEffect(() => {
setAnimatedSegments([]);
setIsAnimating(false);
isAnimatingRef.current = false;
timeoutsRef.current.forEach(clearTimeout);
timeoutsRef.current = [];
}, [osrmRoute]);
const startAnimation = () => {
// Stop if already animating
if (isAnimating) {
setIsAnimating(false);
isAnimatingRef.current = false;
setAnimatedSegments([]);
timeoutsRef.current.forEach(clearTimeout);
timeoutsRef.current = [];
return;
}
const path = osrmRoute || (hasPick && hasDrop ? [pickCoords, dropCoords] : null);
if (!path || path.length < 2) return;
setIsAnimating(true);
isAnimatingRef.current = true;
setAnimatedSegments([]);
timeoutsRef.current.forEach(clearTimeout);
timeoutsRef.current = [];
// Build segments same way as Dispatch.js: path[i] → path[i+1], each with a delay
const allSegs = [];
for (let i = 0; i < path.length - 1; i++) {
allSegs.push({ from: path[i], to: path[i + 1], delay: i * 18 });
}
allSegs.forEach((s, idx) => {
const t = setTimeout(() => {
if (!isAnimatingRef.current) return;
setAnimatedSegments((prev) => [...prev, s]);
if (idx === allSegs.length - 1) {
const finalT = setTimeout(() => {
setIsAnimating(false);
isAnimatingRef.current = false;
}, 800);
timeoutsRef.current.push(finalT);
}
}, s.delay);
timeoutsRef.current.push(t);
});
};
// Cleanup on unmount
useEffect(() => () => {
timeoutsRef.current.forEach(clearTimeout);
isAnimatingRef.current = false;
}, []);
const staticRoute = osrmRoute || (hasPick && hasDrop ? [pickCoords, dropCoords] : null);
return (
<div style={{ position: 'relative', width: '100%', height: '100%', minHeight: '350px' }}>
<MapContainer
center={defaultCenter}
zoom={12}
style={{ width: '100%', height: '100%', minHeight: '350px' }}
zoomControl={true}
>
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
attribution='&copy; OpenStreetMap contributors'
/>
{hasPick && <Marker position={pickCoords} icon={pickupIcon} />}
{hasDrop && <Marker position={dropCoords} icon={dropoffIcon} />}
{/* Static solid polyline (shown when not animating) */}
{!isAnimating && staticRoute && staticRoute.length >= 2 && (
<Polyline
positions={staticRoute}
pathOptions={{ color: '#1890ff', weight: 4, opacity: 0.85, lineJoin: 'round', lineCap: 'round' }}
/>
)}
{/* Animated segments — drawn pair-by-pair, matching Dispatch.js pattern */}
{isAnimating && animatedSegments.map((s, i) => (
<Polyline
key={i}
positions={[s.from, s.to]}
pathOptions={{ color: '#1890ff', weight: 5, opacity: 0.95, lineJoin: 'round', lineCap: 'round' }}
/>
))}
<MapBoundsController startPoint={startPoint} endPoint={endPoint} />
</MapContainer>
{/* Floating Animate Routes button — same style as Dispatch's sbt button */}
{hasPick && hasDrop && (
<button
onClick={startAnimation}
style={{
position: 'absolute',
bottom: '16px',
right: '16px',
zIndex: 1000,
display: 'flex',
alignItems: 'center',
gap: '6px',
padding: '8px 14px',
borderRadius: '8px',
border: 'none',
cursor: 'pointer',
fontWeight: 600,
fontSize: '13px',
boxShadow: '0 4px 14px rgba(0,0,0,0.18)',
background: isAnimating ? '#1890ff' : '#ffffff',
color: isAnimating ? '#ffffff' : '#1a202c',
transition: 'all 0.25s ease',
letterSpacing: '0.01em'
}}
>
<span style={{ fontSize: '14px' }}>{isAnimating ? '⏹' : '▶'}</span>
{isAnimating ? 'Stop' : 'Animate Route'}
</button>
)}
</div>
);
};
function loadScript(src, position, id) {
if (!position) {
return;
}
const script = document.createElement('script');
script.setAttribute('async', '');
script.setAttribute('id', id);
script.src = src;
position.appendChild(script);
}
const Createorder1 = () => {
Geocode.setApiKey(process.env.REACT_APP_GOOGLE_MAPS_API_KEY);
// ================================================= || GoogleMaps (Drawer) || =================================================
const loaded = React.useRef(false);
const navigate = useNavigate();
const theme = useTheme();
const locationRef = useRef(null);
const tenantRef = useRef(null);
const [inputValue1, setInputValue1] = React.useState('');
const [inputValue2, setInputValue2] = React.useState('');
const textFieldRef1 = useRef(null);
const textFieldRef1a = useRef(null);
const textFieldRef2 = useRef(null);
const [appId, setAppId] = useState(0);
const [open, setOpen] = useState(false);
const [startdate, setStartdate] = useState(dayjs().format('MM-DD-YYYY'));
const [starttime, setStatrttime] = useState();
const [endtime, setEndtime] = useState();
const [timeslotarr, setTimeslotarr] = useState([]);
const [otherinstructions, setOtherinstructions] = useState('');
const [loading2, setLoading2] = useState(false);
const [loading, setLoading] = useState(false);
const [btnLoading, setBtnLoading] = useState(false);
const [alertmessage, setAlertmessage] = useState('');
const [admintoken, setAdmintoken] = useState();
const [tenant, setTenant] = useState({});
const [selectedtime, setSelectedtime] = useState('');
const [tenantlist, setTenantlist] = useState([]);
const [startPoint, setStartPoint] = useState({ latitude: 0, longitude: 0 });
const [endPoint, setEndPoint] = useState({ latitude: 0, longitude: 0 });
const [showDistance, setShowDistance] = useState(false);
const [distance, setDistance] = useState(0);
const [basePrice, setBasePrice] = useState(0);
const [pricePerKm, setPricePerKm] = useState(0);
const [minKm, setMinKm] = useState(0);
const [totalCharge, setTotalCharge] = useState(0);
const [subCat, setSubCat] = useState([]);
const [subCatName, setSubCatName] = useState('Select ');
const [subCatId, setSubCatId] = useState(0);
const [weight, setWeight] = useState('');
const [tenantid, setTenantid] = useState(0);
const [locationid, setLocationid] = useState(0);
const [selectedCatChip, setSelectedCatChip] = useState(null);
const [isCustomerOpen, setIsCustomerOpen] = useState(false);
const [searchCustList, setSearchCustList] = useState('');
const [customerlist, setCustomerlist] = useState([]);
const [pickCust, setPickCust] = useState(null);
const [dropCust, setDropCust] = useState(null);
const [pickordrop, setpickordrop] = useState(0); // 1 ->pick 2 -> drop
const [addId1, setAddId1] = useState(0);
const [addId2, setAddId2] = useState(0);
const [tenantLocations, setTenantlocations] = useState([]);
const [appLocaLat, setAppLocaLat] = useState();
const [appLocaLng, setAppLocaLng] = useState();
const [appLocaRadius, setAppLocaRadius] = useState();
const [locations, setLocations] = useState('Select Location');
const userid = localStorage.getItem('userid');
const [isNumChange1, setIsNumChange1] = useState(0);
const [isNumChange2, setIsNumChange2] = useState(0);
const [showCheck1, setShowCheck1] = useState(0);
const [showCheck2, setShowCheck2] = useState(0);
const [pickNum, setPickNum] = useState();
const [dropNum, setdropNum] = useState();
const [numErr1, setNumErr1] = useState(false);
const [numErr2, setNumErr2] = useState(false);
const [isSms, setIsSms] = useState(0);
const [collectionamt, setCollectionamt] = useState(0);
const [quantity, setQuantity] = useState(1);
const [tenantValue, setTenantValue] = useState(null);
const [locationValue, setLocationValue] = useState(null);
const [pickupdate, setPickupdate] = useState(dayjs().add(5, 'minute'));
const [pickDialog, setPickDialog] = useState(false);
const [deliverydate, setDeliverydate] = useState(dayjs().add(30, 'minute'));
const [deliveryDialog, setDeliveryDialog] = useState(false);
const [pickupSearchOpen, setPickupSearchOpen] = useState(false);
const [dropSearchOpen, setDropSearchOpen] = useState(false);
const pickerRef = useRef(null);
const deliveryRef = useRef(null);
const handlePickupChange = (newValue) => {
if (!newValue) return;
setPickupdate(newValue);
if (deliverydate.isBefore(newValue)) {
setDeliverydate(newValue.add(30, 'minute'));
}
};
if (typeof window !== 'undefined' && !loaded.current) {
if (!document.querySelector('#google-maps')) {
loadScript(
`https://maps.googleapis.com/maps/api/js?key=${process.env.REACT_APP_GOOGLE_MAPS_API_KEY}&libraries=places&location=10.3656,77.9690&radius=50000&components=country:IN&strictbounds=true`,
document.querySelector('head'),
'google-maps'
);
}
loaded.current = true;
}
// ==============================|| fetchAppLocations ||============================== //
const fetchAppLocations = async () => {
try {
const locationRes = await axios.get(`${process.env.REACT_APP_URL}/partners/getlocations/?userid=${userid}`);
console.log('fetchAppLocations', locationRes.data.details);
setLocations(locationRes.data.details);
} catch (err) {
console.log('locationRes', err);
}
};
useEffect(() => {
fetchAppLocations();
}, []);
// ===================================================== || fetchtenantinfolist || =====================================================
const fetchtenantinfolist = async () => {
setLoading(true);
await axios
.get(`${process.env.REACT_APP_URL}/tenants/gettenants/?applocationid=${appId}&status=active`)
.then((res) => {
console.log(res);
if (res.data.status) {
let arr = [];
res.data.details.map((val) => {
arr.push({
...val,
label: `${val.tenantname}`
});
});
setTenantlist(arr);
}
setLoading(false);
})
.catch((err) => {
console.log(err);
setLoading(false);
});
};
useEffect(() => {
appId && fetchtenantinfolist();
}, [appId]);
const handleChipClick = (chipLabel) => {
setSelectedCatChip(chipLabel);
};
const chipStyle = (chipLabel) => ({
cursor: 'pointer',
backgroundColor: selectedCatChip === chipLabel ? theme.palette.primary.main : 'default',
color: selectedCatChip === chipLabel ? '#fff' : '',
'&:hover': {
backgroundColor: selectedCatChip === chipLabel ? theme.palette.primary.main : theme.palette.primary.light,
color: '#fff'
}
});
const fetchTenantPricing = async (id) => {
try {
const pricingResponse = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenantpricing/?tenantid=${id}`);
console.log('pricingResponse', pricingResponse.data.details);
setBasePrice(pricingResponse.data.details.baseprice);
setPricePerKm(pricingResponse.data.details.priceperkm);
setMinKm(pricingResponse.data.details.minkm);
} catch (error) {
console.log('fetchTenantPricing error', error);
}
};
useEffect(() => {
console.log('startPoint', startPoint);
console.log('endPoint', endPoint);
if (startPoint.latitude != 0 && startPoint.longitude != 0 && endPoint.latitude != 0 && endPoint.longitude != 0) {
// getDistance();
calculateDistance(startPoint, endPoint);
}
}, [startPoint, endPoint]);
const calculateDistance = async (pickup, drop) => {
const service = new google.maps.DistanceMatrixService();
const getDistanceMatrix = (origins, destinations, travelMode, unitSystem) => {
return new Promise((resolve, reject) => {
service.getDistanceMatrix(
{
origins: [new google.maps.LatLng(origins.latitude, origins.longitude)],
destinations: [new google.maps.LatLng(destinations.latitude, destinations.longitude)],
travelMode: travelMode,
unitSystem: unitSystem
},
(response, status) => {
if (status === 'OK') {
resolve(response);
} else {
reject(new Error(`Error calculating distance: ${status}`));
}
}
);
});
};
try {
// Use await to wait for the promise to resolve
const response = await getDistanceMatrix(pickup, drop, 'DRIVING', google.maps.UnitSystem.METRIC);
// Handle the response
const results = response.rows[0].elements;
for (let i = 0; i < results.length; i++) {
const element = results[i];
// Extract the numerical value of the distance
const distance = element.distance.value;
console.log('distance in m ', distance);
const distanceInKm = (distance / 1000).toFixed(2);
console.log('distance in km ', distanceInKm);
const roundedDistance = Math.round(distanceInKm);
console.log('roundedDistance', roundedDistance);
setDistance(roundedDistance);
if (roundedDistance < minKm) {
setTotalCharge(basePrice);
} else {
console.log('minKm', minKm);
console.log('pricePerKm', pricePerKm);
console.log('basePrice', basePrice);
const total = (roundedDistance - minKm) * pricePerKm + basePrice;
console.log('total', total);
setTotalCharge(total);
}
setShowDistance(true);
if (roundedDistance > appLocaRadius) {
setShowDistance(true);
setOpen(true);
}
// Extract the numerical value of the duration
const durationMatch = element.duration.text.match(/([\d.]+)/);
const duration = durationMatch ? parseInt(durationMatch[0]) : null;
// Display only the numerical values
console.log(`Distance: ${roundedDistance}, Duration: ${duration}`);
}
} catch (error) {
console.error('Error calculating distance:', error);
}
};
useEffect(() => {
if (tenantid) {
clientdetails();
}
}, [searchCustList?.length > 3, searchCustList == '', tenantid, pickupSearchOpen, dropSearchOpen]);
useEffect(() => {
if (timeslotarr[0]) {
let arr = [];
timeslotarr.map((val) => {
if (dayjs().diff(dayjs(`${dayjs(startdate).format('MM-DD-YYYY')} ${dayjs(val).format('HH:mm:ss')}`), 'm') <= 0) {
arr.push(val);
}
});
}
}, [timeslotarr]);
// ==================================================== || fetchtenantinfo || ====================================================
const fetchtenantinfo = async () => {
setLoading(true);
console.log('tenantid', tenantid);
await axios
.get(`${process.env.REACT_APP_URL}/tenants/gettenantinfo/?tenantid=${tenantid}`)
.then((res) => {
console.log('fetchtenantinfo', res);
if (res.data.status) {
setTenant(res.data.details);
fetchAppAdminTokens();
setSubCatName(res.data.details.subcategoryname);
setSubCatId(res.data.details.subcategoryid);
}
setLoading(false);
})
.catch((err) => {
console.log(err);
setLoading(false);
});
};
useEffect(() => {
if (tenantid) {
fetchtenantinfo();
}
}, [tenantid]);
// ==================================================== || getsubcategories || ====================================================
const getsubcategories = async () => {
await axios
.get(`${process.env.REACT_APP_URL}/utils/getsubcategories/?moduleid=6`)
.then((res) => {
console.log('subcateRes', res.data.details);
if (res.data.status) {
setSubCat(res.data.details);
}
})
.catch((err) => {
console.log(err);
});
};
useEffect(() => {
getsubcategories();
}, []);
// ==================================================== || fetchTiming || ====================================================
const fetchTiming = async () => {
setLoading(true);
await axios
.get(`${process.env.REACT_APP_URL}/utils/getapplocations/?applocationid=${appId}`)
.then((res) => {
console.log('fetchTiming', res);
const { opentime, closetime, latitude, longitude, radius } = res.data.details[0];
if (res.data.status) {
setAppLocaLat(latitude);
setAppLocaLng(longitude);
setAppLocaRadius(radius);
console.log('radius', radius);
setStatrttime(`${dayjs().format('MM-DD-YYYY')} ${opentime}`);
setEndtime(`${dayjs().format('MM-DD-YYYY')} ${closetime}`);
console.log('starttime', `${dayjs().format('MM-DD-YYYY')} ${opentime}`);
console.log('endtime', `${dayjs().format('MM-DD-YYYY')} ${closetime} `);
let arr = [];
for (
let i = `${dayjs().format('MM-DD-YYYY')} ${opentime}`, j = 0;
dayjs(`${dayjs().format('MM-DD-YYYY')} ${closetime} `).diff(i, 'm') >= 0;
j++, i = dayjs(i).add(30, 'm')
) {
arr.push(i);
}
console.log('setTimeslotarr', arr);
setTimeslotarr(arr);
}
setLoading(false);
})
.catch((err) => {
console.log(err);
setLoading(false);
});
};
useEffect(() => {
if (appId) {
fetchTiming();
}
}, [appId]);
// =============================================== || fetchAppAdminTokens (via appId) || ===============================================
const fetchAppAdminTokens = async () => {
setLoading(true);
await axios
.get(`${process.env.REACT_APP_URL}/utils/getapplocationconfig/?applocationid=${appId}`)
.then((res) => {
const userfcmtokemArray = res.data.details.applocationadmins.map((admin) => admin.userfcmtokem); // fcm => firebase cloud messaging
console.log('fetchAppAdminTokens', res);
console.log('userfcmtokemArray', userfcmtokemArray);
if (res.data.status) {
setAdmintoken(userfcmtokemArray);
}
setLoading(false);
})
.catch((err) => {
console.log(err);
setLoading(false);
});
};
useEffect(() => {
if (starttime && endtime) {
fetchAppAdminTokens();
}
}, [starttime, endtime]);
// =============================================== || opentoast || ===============================================
const opentoast = (message, variant, time) => {
enqueueSnackbar(message, {
variant: variant,
anchorOrigin: { vertical: 'top', horizontal: 'right' },
autoHideDuration: time ? time : 1500
});
console.log(alertmessage);
};
const createsubmitobj2 = async () => {
let arr = {};
arr = {
orders: {
applocationid: tenant.applolcationid,
cancellled: '',
categoryid: +tenant.categoryid,
configid: 9,
customerid: isNumChange1 == 0 ? +pickCust.customerid || 0 : 0,
deliveryaddress: dropCust.address || '',
deliverycharge: +totalCharge.toFixed(2) || 0,
deliverycity: dropCust.city || '',
deliverycontactno: dropCust.contactno || '',
deliverycustomer: dropCust.firstname || '',
deliveryid: isNumChange2 == 0 ? +dropCust.customerid || 0 : 0,
deliverylandmark: dropCust.landmark || '',
deliverylat: dropCust.latitude.toString(),
deliverylocation: dropCust.suburb || '',
deliverylocationid: dropCust.deliverylocationid || 0,
deliverylong: dropCust.longitude.toString(),
deliverytime: `${dayjs(startdate).format('YYYY-MM-DD')} ${dayjs(selectedtime.$d).format('HH:mm:ss')}`,
deliverytype: 'B',
delivered: '',
itemcount: 1,
kms: distance.toString() || 0,
locationid: +locationid,
moduleid: +tenant.moduleid,
orderamount: +totalCharge.toFixed(2) || 0,
ordercharges: 0.0,
orderdate: dayjs().format('YYYY-MM-DD HH:mm:ss'),
ordernotes: otherinstructions,
orderstatus: 'created',
ordervalue: +totalCharge.toFixed(2) || 0,
partnerid: tenant.partnerid,
partneruserid: +userid,
paymentstatus: 1,
paymenttype: 42,
pickupaddress: pickCust.address || '',
pickupcity: pickCust.city || '',
pickupcontactno: pickCust.contactno || '',
pickupcustomer: pickCust.firstname || '',
pickuplandmark: pickCust.landmark || '',
pickuplat: pickCust.latitude.toString() || '',
pickuplocation: pickCust.suburb || '',
pickuplocationid: pickCust.deliverylocationid || 0,
pickuplong: pickCust.longitude.toString() || '',
smsdelivery: isSms,
subcategoryid: +subCatId,
tenantid: tenant.tenantid,
collectionamt: +collectionamt,
quantity: +quantity,
weight
},
pickup: {
address: pickCust.address || '',
applocationid: tenant.applolcationid,
city: pickCust.city || '',
configid: 1,
contactno: pickCust.contactno || '',
customertoken: '',
customerid: isNumChange1 == 0 ? pickCust.customerid || 0 : 0,
devicetype: '',
deviceid: '',
dialcode: '+91',
doorno: pickCust.doorno || '',
email: pickCust.email || '',
firstname: pickCust.firstname || '',
landmark: pickCust.landmark || '',
latitude: pickCust.latitude.toString() || '',
longitude: pickCust.longitude.toString() || '',
postcode: pickCust.postcode || '',
primaryaddress: 1,
locationid: pickCust.deliverylocationid || 0,
profileimage: '',
state: pickCust.state || '',
suburb: pickCust.suburb || '',
tenantid: tenant.tenantid
},
drop: {
address: dropCust.address || '',
applocationid: tenant.applolcationid,
city: dropCust.city || '',
configid: 1,
contactno: dropCust.contactno || '',
customertoken: '',
customerid: isNumChange2 == 0 ? dropCust.customerid || 0 : 0,
devicetype: '',
deviceid: '',
locationid: dropCust.deliverylocationid || 0,
dialcode: '+91',
doorno: dropCust.doorno || '',
email: dropCust.email || '',
firstname: dropCust.firstname || '',
landmark: dropCust.landmark || '',
latitude: dropCust.latitude.toString(),
longitude: dropCust.longitude.toString(),
postcode: dropCust.postcode || '',
primaryaddress: 1,
profileimage: '',
state: dropCust.state || '',
suburb: dropCust.suburb || '',
tenantid: tenant.tenantid
}
};
console.log('createsubmitobj2', arr);
if (!pickCust.firstname) {
opentoast('Enter Pickup Contact Name ', 'warning', 2000);
} else if (!pickCust.contactno) {
opentoast('Enter Pickup Contact Number ', 'warning', 2000);
} else if (pickCust.contactno.length != 10) {
opentoast('Check Pickup Contact Number ', 'error', 2000);
} else if (!pickCust.suburb) {
opentoast('Enter Pickup Location ', 'warning', 2000);
}
// else if (!pickCust.city) {
// opentoast('Enter Pickup City ', 'warning', 2000);
// }
else if (!pickCust.postcode) {
opentoast('Enter Pickup Postcode ', 'warning', 2000);
} else if (!pickCust.landmark) {
opentoast('Enter Pickup Landmark ', 'warning', 2000);
} else if (!dropCust.firstname) {
opentoast('Enter Drop Contact Name ', 'warning', 2000);
} else if (!dropCust.contactno) {
opentoast('Enter Drop Contact Number', 'warning', 2000);
} else if (dropCust.contactno.length !== 10) {
opentoast('Check Drop Contact Number ', 'error', 2000);
} else if (!dropCust.suburb) {
opentoast('Enter Drop Suburb ', 'warning', 2000);
}
// else if (!dropCust.city) {
// opentoast('Enter Drop City ', 'warning', 2000);
// }
else if (!dropCust.postcode) {
opentoast('Enter Drop postcode ', 'warning', 2000);
} else if (!dropCust.landmark) {
opentoast('Enter Drop Landmark ', 'warning', 2000);
} else if (!selectedtime) {
opentoast('Choose deliverytime ', 'warning', 2000);
} else if (!setSubCatId) {
opentoast('Choose SubCategory ', 'warning', 2000);
} else {
try {
const createRes = await axios.post(`${process.env.REACT_APP_URL2}/orders/createorder`, arr);
if (createRes.data.status) {
console.log('createRes', createRes);
enqueueSnackbar('Order Created Successfully', {
variant: 'success',
anchorOrigin: { vertical: 'top', horizontal: 'right' },
autoHideDuration: 1000
});
if (admintoken) {
// notifyadmin(admintoken);
sendnotifications();
}
navigate('/nearle/orders');
} else {
opentoast('Error in creating orders', 'warning');
}
setLoading(false);
} catch (error) {
opentoast(error.message, 'warning');
console.log('createResErr', error);
}
}
};
// ========================================================= || clientdetails || =========================================================
const clientdetails = async () => {
setLoading2(true);
try {
let url =
searchCustList == ''
? `${process.env.REACT_APP_URL}/customers/gettenantcustomers/?tenantid=${tenantid}&pageno=1&pagesize=30`
: `${process.env.REACT_APP_URL}/customers/search/?tenantid=${tenantid}&keyword=${searchCustList}`;
await axios
.get(url)
.then((res) => {
if (res.data.status) {
console.log('clientdetails', res.data.details);
setCustomerlist(res.data.details);
}
setLoading2(false);
})
.catch((err) => {
console.log(err);
setLoading2(false);
opentoast('server error', 'warning');
});
} catch (err) {
console.log(err);
setLoading2(false);
}
};
// ================================================== || sendnotifications || ==================================================
const sendnotifications = async () => {
setLoading(true);
await axios
.post(`${process.env.REACT_APP_URL}/utils/sendnotifications`, {
priority: 'high',
registration_ids: admintoken,
data: {
accessid: process.env.REACT_APP_RIDER_ACCESS_ID
},
notification: {
title: 'Nearle Merchant',
body: 'An Order has been placed successfully,kindly process the same',
sound: 'ring'
}
})
.then((res) => {
console.log(res);
if (res.data.message == 'Success') {
enqueueSnackbar('Notification sent Successfully', {
variant: 'success',
anchorOrigin: { vertical: 'top', horizontal: 'right' },
autoHideDuration: 1000
});
}
setLoading(false);
})
.catch((err) => {
console.log(err);
enqueueSnackbar(err.message, {
variant: 'error',
anchorOrigin: { vertical: 'top', horizontal: 'right' },
autoHideDuration: 1000
});
setLoading(false);
});
};
// ============================================= || Google Maps Autocomplete(pick) || =============================================
useEffect(() => {
// Initialize Google Maps Autocomplete
if (inputValue1) {
const autocompleteInput = document.getElementById('addressAuto1');
const autocomplete = new window.google.maps.places.Autocomplete(autocompleteInput, {
// types: ['(cities)'], // You can adjust the types parameter based on your requirements
strictBounds: true,
bounds: new window.google.maps.Circle({
// center: new window.google.maps.LatLng(11.0050707, 76.9509083),
// radius: 100000
center: new window.google.maps.LatLng(appLocaLat, appLocaLng),
radius: appLocaRadius * 1000
}).getBounds()
});
// Event listener for autocomplete place changed
autocomplete.addListener('place_changed', () => {
const place = autocomplete.getPlace();
setInputValue1(`${place.name}, ${place.formatted_address}`);
console.log('new place', place); // Do something with the selected place
console.log(' pick (new place) lat lng', { lat: place.geometry.location.lat(), lng: place.geometry.location.lng() }); // Do something with the selected place
// to trigger getDistance
setStartPoint({ latitude: place.geometry.location.lat(), longitude: place.geometry.location.lng() });
setPickCust({ ...pickCust, address: `${place.name} ${place.formatted_address}` });
const address = {
address: `${place.name} ${place.formatted_address}`,
street_number: '',
route: '',
locality: '',
sublocality_level_1: '',
administrative_area_level_3: '',
administrative_area_level_1: '',
country: '',
postal_code: ''
};
place.address_components.forEach((component) => {
component.types.forEach((type) => {
switch (type) {
case 'street_number':
address.street_number = component.long_name;
break;
case 'route':
address.route = component.long_name;
break;
case 'locality':
address.locality = component.long_name;
break;
case 'sublocality_level_1':
address.sublocality_level_1 = component.long_name;
break;
case 'administrative_area_level_3':
address.administrative_area_level_3 = component.long_name;
break;
case 'administrative_area_level_1':
address.administrative_area_level_1 = component.long_name;
break;
case 'country':
address.country = component.long_name;
break;
case 'postal_code':
address.postal_code = component.long_name;
break;
// Add more cases as needed for other types
}
});
});
// Use address object as per your requirements
setPickCust({
...pickCust,
address: address.address,
doorno: `${address.street_number} ${address.route}`,
suburb: address.sublocality_level_1,
city: address.locality,
postcode: address.postal_code,
latitude: place.geometry.location.lat(),
longitude: place.geometry.location.lng()
});
console.log('Pick Address:', address);
});
}
}, [inputValue1]);
// ============================================= || Google Maps Autocomplete(Drop) || =============================================
useEffect(() => {
if (inputValue2) {
// Initialize Google Maps Autocomplete
const autocompleteInput = document.getElementById('addressAuto2');
const autocomplete = new window.google.maps.places.Autocomplete(autocompleteInput, {
// types: ['(cities)'], // You can adjust the types parameter based on your requirements
strictBounds: true,
bounds: new window.google.maps.Circle({
// center: new window.google.maps.LatLng(11.0050707, 76.9509083),
center: new window.google.maps.LatLng(appLocaLat, appLocaLng),
radius: appLocaRadius * 1000 //km to m
// radius: 100000 //km to m
}).getBounds()
});
let arr = [];
// Event listener for autocomplete place changed
autocomplete.addListener('place_changed', () => {
const place = autocomplete.getPlace();
setInputValue2(`${place.name}, ${place.formatted_address}`);
console.log('new place', place); // Do something with the selected place
console.log('drop (new place) lat lng', { lat: place.geometry.location.lat(), lng: place.geometry.location.lng() }); // Do something with the selected place
setEndPoint({ latitude: place.geometry.location.lat(), longitude: place.geometry.location.lng() });
setDropCust({ ...dropCust, address: `${place.name} ${place.formatted_address}` });
const address = {
address: `${place.name} ${place.formatted_address}`,
street_number: '',
route: '',
locality: '',
sublocality_level_1: '',
administrative_area_level_3: '',
administrative_area_level_1: '',
country: '',
postal_code: ''
};
place.address_components.forEach((component) => {
component.types.forEach((type) => {
switch (type) {
case 'street_number':
address.street_number = component.long_name;
break;
case 'route':
address.route = component.long_name;
break;
case 'locality':
address.locality = component.long_name;
break;
case 'sublocality_level_1':
address.sublocality_level_1 = component.long_name;
break;
case 'administrative_area_level_3':
address.administrative_area_level_3 = component.long_name;
break;
case 'administrative_area_level_1':
address.administrative_area_level_1 = component.long_name;
break;
case 'country':
address.country = component.long_name;
break;
case 'postal_code':
address.postal_code = component.long_name;
break;
// Add more cases as needed for other types
}
});
});
// Use address object as per your requirements
setDropCust({
...dropCust,
address: address.address,
doorno: `${address.street_number} ${address.route}`,
suburb: address.sublocality_level_1,
city: address.locality,
postcode: address.postal_code,
latitude: place.geometry.location.lat(),
longitude: place.geometry.location.lng()
});
console.log('Drop Address:', address);
});
}
}, [inputValue2]);
// ============================================= || gettenantlocations (branches) || =============================================
const gettenantlocations = async (id) => {
try {
const res = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenantlocations/?tenantid=${id}`);
console.log('gettenantlocations', res.data.details);
if (res.data.details.length == 1) {
setTenantlocations(res.data.details);
setLocationid(res.data.details[0].locationid);
setLocationValue(res.data.details[0].locationid);
} else {
setTenantlocations(res.data.details);
}
} catch (err) {
console.log('gettenantlocations', err);
}
};
return (
<>
{loading && <Loader />}
{loading2 && <Loader />}
<TitleCard title={'Create Order'} sx={{ mb: 3 }} />
<Grid
container
spacing={3}
sx={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'start',
alignItems: 'center',
spacing: 5
}}
>
<Grid item xs={12} sx={{ width: '100%', mt: -2 }}>
<Stack sx={{ width: '100%', my: 2 }} direction={'row'} alignItems={'center'} gap={2} justifyContent={'start'}>
<Avatar size="small" sx={{ bgcolor: 'primary.main', width: 34, height: 34 }}>
1
</Avatar>
<Typography variant="h4">Select Location and Client</Typography>
</Stack>
<MainCard sx={{ height: '100%' }}>
<Grid container spacing={3} sx={{}}>
{/* ===================================================== || Choose App location || ===================================================== */}
<Grid item xs={12} sm={4}>
<Autocomplete
fullWidth
autoFocus
ref={locationRef}
options={locations || []}
getOptionLabel={(option) => `${option.locationname}`}
onChange={(event, value, reason) => {
if (reason === 'clear') {
setAppId(0);
setTenantid(0);
setTenantValue(null);
setTenantlist([]);
setLocationid(0);
setLocationValue(null);
setTenantlocations([]);
setPickCust(null);
setDropCust(null);
} else {
setAppId(value.applocationid);
setPickCust(null);
setDropCust(null);
setTenantid(0);
setTenantValue(null);
setTenantlist([]);
setLocationid(0);
setLocationValue(null);
setTenantlocations([]);
}
}}
renderInput={(params) => <TextField {...params} label={'Choose Location'} />}
/>
</Grid>
{/* ===================================================== || Choose client || ===================================================== */}
<Grid item xs={12} sm={4}>
<Autocomplete
fullWidth
options={tenantlist || []}
value={tenantValue}
onOpen={(event) => {
if (!appId) {
event.preventDefault();
OpenToast('Please select a your location first!', 'warning', 3000);
setTimeout(() => {
locationRef.current?.focus();
}, 0);
}
}}
onChange={(e, val, reason) => {
if (reason == 'clear') {
setTenantid(0);
setTenantValue(null);
setLocationid(0);
setLocationValue(null);
setTenantlocations([]);
setPickCust(null);
setDropCust(null);
} else {
setTenantid(val?.tenantid || 0);
setTenantValue(val);
setLocationid(0);
setLocationValue(null);
fetchTenantPricing(val.tenantid);
gettenantlocations(val.tenantid);
setDropCust(null);
}
}}
renderInput={(params) => <TextField {...params} label="Choose Client" inputRef={tenantRef} />}
/>
</Grid>
{/* ===================================================== ||Business Location || ===================================================== */}
<Grid item xs={12} sm={4}>
{tenantLocations.length == 1 ? (
<TextField
variant="outlined"
fullWidth
label={'Business Location'}
value={tenantLocations[0].locationname}
InputProps={{
style: { color: theme.palette.primary.main },
startAdornment: (
<InputAdornment position="start">
<MyLocationIcon color="primary" />
</InputAdornment>
)
}}
/>
) : (
<Autocomplete
fullWidth
value={locationValue}
options={tenantLocations || []}
getOptionLabel={(option) => `${option.locationname} (${option.suburb})` || ''}
onOpen={(event) => {
if (!appId && !tenantid) {
event.preventDefault();
OpenToast('Please select Location and Tenant first!', 'warning', 3000);
setTimeout(() => {
locationRef.current?.focus();
}, 0);
} else if (!tenantid) {
event.preventDefault();
OpenToast('Please select a your Tenant first!', 'warning', 3000);
setTimeout(() => {
tenantRef.current?.focus();
}, 0);
}
}}
onChange={(event, value, reason) => {
if (reason === 'clear') {
setLocationid(0);
setLocationValue(null);
setPickCust(null);
} else {
setLocationid(value.locationid || 0);
setLocationValue(value);
}
}}
renderInput={(params) => <TextField {...params} label="Select Business Locations" color="primary" />}
/>
)}
</Grid>
</Grid>
</MainCard>
</Grid>
</Grid>
{/* ================================================= || Pickup & Drop || ================================================= */}
<Grid container sx={{}}>
<Grid item xs={12} sx={{ mt: 2 }}>
<Stack sx={{ width: '100%', my: 2 }} direction={'row'} alignItems={'center'} gap={2} justifyContent={'start'}>
<Avatar size="small" sx={{ bgcolor: 'primary.main', width: 34, height: 34 }}>
2
</Avatar>
<Typography variant="h4">Pickup & Drop Details</Typography>
</Stack>
<Grid container spacing={1}>
<Grid item xs={12}>
<Grid container spacing={2} sx={{ height: '100%' }}>
<Grid item xs={12}>
<Grid container spacing={2}>
<Grid item xs={12} md={8}>
<Grid container spacing={2} sx={{ height: '100%' }}>
{/* ================================================= || Pickup || ================================================= */}
<Grid item xs={12} sm={6}>
<MainCard sx={{ height: '100%' }}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Stack direction={'row'} justifyContent={'space-between'} alignItems={'center'}>
<Typography variant="h5" sx={{ mb: 0 }}>
Pickup Details
</Typography>
<Stack display={'flex'} direction={'row'} gap={1} alignItems={'center'}>
{pickCust?.firstname && (
<Typography variant="caption" sx={{ color: 'success.main', fontWeight: 600 }}>
Selected
</Typography>
)}
<Button
variant={pickupSearchOpen ? 'outlined' : 'contained'}
size="small"
disabled={!locationid}
onClick={() => {
if (!appId && !tenantid && !locationid) {
OpenToast('Please select Location, Tenant and Business!', 'warning', 3000);
} else if (!appId && !tenantid) {
OpenToast('Please select Location and Tenant!', 'warning', 3000);
} else if (!appId) {
OpenToast('Please select Location!', 'warning', 3000);
} else {
setPickupSearchOpen((prev) => !prev);
setDropSearchOpen(false);
setSearchCustList('');
setpickordrop(1);
}
}}
sx={{ minWidth: 110 }}
>
{pickupSearchOpen ? 'Close' : 'Select Pickup'}
</Button>
</Stack>
</Stack>
{/* ============ Inline Pickup Search Panel ============ */}
<div
style={{
overflow: 'hidden',
maxHeight: pickupSearchOpen ? '380px' : '0px',
transition: 'max-height 0.35s cubic-bezier(0.4, 0, 0.2, 1)',
marginTop: pickupSearchOpen ? '12px' : '0px'
}}
>
<div
style={{
border: '1.5px solid #e2e8f0',
borderRadius: '12px',
background: '#f8fafc',
padding: '12px',
display: 'flex',
flexDirection: 'column',
gap: '8px'
}}
>
{/* Search Input */}
<OutlinedInput
autoFocus={pickupSearchOpen}
fullWidth
size="small"
placeholder="Search customer..."
value={pickupSearchOpen ? searchCustList : ''}
onChange={(e) => setSearchCustList(e.target.value)}
sx={{ bgcolor: 'white', borderRadius: '8px' }}
startAdornment={
<InputAdornment position="start">
<SearchOutlined style={{ fontSize: 'small' }} />
</InputAdornment>
}
endAdornment={
searchCustList ? (
<IconButton size="small" onClick={() => setSearchCustList('')}>
<ClearIcon fontSize="small" />
</IconButton>
) : null
}
/>
{/* Customer List */}
<div
style={{
maxHeight: '280px',
overflowY: 'auto',
display: 'flex',
flexDirection: 'column',
gap: '6px'
}}
>
{customerlist?.length === 0 ? (
<Stack alignItems="center" justifyContent="center" sx={{ py: 2 }}>
<Empty />
</Stack>
) : (
customerlist?.map((address, index) => (
<Button
key={index}
disabled={pickCust?.customerid === address.customerid}
onClick={() => {
setPickupSearchOpen(false);
setAddId1(1);
setStartPoint({ latitude: address.latitude, longitude: address.longitude });
setPickCust(address);
setPickNum(address.contactno);
setSearchCustList('');
}}
sx={{
width: '100%',
border: '1.5px solid',
borderColor: pickCust?.customerid === address.customerid ? 'primary.main' : 'grey.200',
borderRadius: '8px',
p: 1.25,
textAlign: 'left',
justifyContent: 'flex-start',
bgcolor: pickCust?.customerid === address.customerid ? 'primary.lighter' : 'white',
'&:hover': { borderColor: 'primary.main', bgcolor: 'rgba(24,144,255,0.04)' }
}}
>
<div style={{ width: '100%' }}>
<Typography variant="subtitle2" sx={{ textAlign: 'left', fontWeight: 600 }}>
{`${address.firstname} (${address.contactno})`}
</Typography>
<Typography variant="caption" color="secondary" sx={{ textAlign: 'left', display: 'block' }}>
{address.address}
</Typography>
</div>
</Button>
))
)}
</div>
</div>
</div>
</Grid>
<Grid item xs={12} sx={{}}>
<Grid container spacing={4}>
{/* ====================================== ||Contact Name (pick) || ====================================== */}
<Grid item xs={12} sm={6}>
<TextField
inputRef={textFieldRef1}
// disabled={!appId || !tenantid || !locationid}
fullWidth
InputProps={{
startAdornment: (
<IconButton>
<FaUser />
</IconButton>
)
}}
variant="outlined"
label="Contact Name"
value={pickCust?.firstname || ''}
onChange={(e) => {
setPickCust({ ...pickCust, firstname: e.target.value });
}}
/>
</Grid>
{/* ====================================== ||Contact Number(pick) || ====================================== */}
<Grid item xs={12} sm={6}>
<TextField
error={numErr1}
inputRef={textFieldRef1a}
// disabled={!appId || !tenantid || !locationid}
fullWidth
type="number"
InputProps={{
inputProps: {
maxLength: 10
},
startAdornment: (
<IconButton>
<FaPhoneAlt color={numErr1 && 'red'} />
</IconButton>
)
}}
variant="outlined"
label="Contact Number"
value={pickCust?.contactno || ''}
onChange={(e) => {
if (e.target.value.length <= 10) {
setPickCust({ ...pickCust, contactno: e.target.value });
}
if (pickNum == e.target.value) {
setShowCheck1(0);
} else {
setShowCheck1(1);
}
if (e.target.value.length < 10) {
setNumErr1(true);
} else {
setNumErr1(false);
}
}}
/>
</Grid>
{/* ====================================== || Address(pick) || ====================================== */}
<Grid item xs={12}>
<Stack spacing={1.25} sx={{ mt: 0 }}>
{addId1 == 0 ? (
<div>
<TextField
// disabled={!appId || !tenantid || !locationid}
id="addressAuto1"
fullWidth
label={'Address'}
variant="outlined"
value={inputValue1 || ''}
onChange={(e) => setInputValue1(e.target.value)}
InputProps={{
endAdornment: (
<IconButton
onClick={() => {
setInputValue1('');
setPickCust({
...pickCust,
doorno: '',
suburb: '',
city: '',
postcode: '',
landmark: ''
});
setShowDistance(false);
setStartPoint({ latitude: 0, longitude: 0 });
}}
size="small"
>
<CloseIcon />
</IconButton>
)
}}
/>
</div>
) : (
<TextField
variant="outlined"
InputProps={{
endAdornment: (
<IconButton
onClick={() => {
setAddId1(0);
setPickCust({
...pickCust,
doorno: '',
suburb: '',
city: '',
postcode: '',
landmark: ''
});
setShowDistance(false);
setStartPoint({ latitude: 0, longitude: 0 });
}}
>
<ClearIcon />
</IconButton>
)
}}
value={pickCust?.address || ''}
onChange={(e) => {
setPickCust({ ...pickCust, address: e.target.value });
if (e.target.value == '') {
setAddId1(0);
setShowDistance(false);
setStartPoint({ latitude: 0, longitude: 0 });
}
}}
/>
)}
</Stack>
</Grid>
{/* ====================================== ||Door No (pick) || ====================================== */}
<Grid item xs={12} sm={6}>
<TextField
// disabled={!appId || !tenantid || !locationid}
fullWidth
InputProps={{
startAdornment: (
<IconButton>
<GiDoorHandle />
</IconButton>
)
}}
variant="outlined"
label="Door No / Street"
value={pickCust?.doorno || ''}
onChange={(e) => {
setPickCust({ ...pickCust, doorno: e.target.value });
}}
/>
</Grid>
{/* ====================================== || Suburb (pick) || ====================================== */}
<Grid item xs={12} sm={6}>
<TextField
// disabled={!appId || !tenantid || !locationid}
fullWidth
InputProps={{
startAdornment: (
<IconButton>
<FaLocationDot />
</IconButton>
)
}}
variant="outlined"
label="Location"
value={pickCust?.suburb || ''}
onChange={(e) => {
setPickCust({ ...pickCust, suburb: e.target.value });
}}
/>
</Grid>
{/* ====================================== || postcode (pick) || ====================================== */}
<Grid item xs={12} sm={6}>
<TextField
// disabled={!appId || !tenantid || !locationid}
fullWidth
InputProps={{
startAdornment: (
<IconButton>
<TbMapPinCode />
</IconButton>
)
}}
variant="outlined"
label="Postcode"
value={pickCust?.postcode || ''}
onChange={(e) => {
setPickCust({ ...pickCust, postcode: e.target.value });
}}
/>
</Grid>
{/* ====================================== || Landmark (pick) || ====================================== */}
<Grid item xs={12} sm={6}>
<TextField
// disabled={!appId || !tenantid || !locationid}
fullWidth
InputProps={{
startAdornment: (
<IconButton>
<FaLandmarkDome />
</IconButton>
)
}}
variant="outlined"
label="Landmark"
value={pickCust?.landmark || ''}
onChange={(e) => {
setPickCust({ ...pickCust, landmark: e.target.value });
}}
/>
</Grid>
{/* ====================================== ||Checkbox save for later (pick) || ====================================== */}
{showCheck1 == 1 && (
<Grid item xs={12} sx={{ display: 'flex', justifyContent: 'end' }}>
<FormGroup>
<FormControlLabel
control={
<Checkbox
checked={isNumChange1 === 1}
onChange={(e) => {
setIsNumChange1(e.target.checked ? 1 : 0);
}}
/>
}
label="Save For Later"
/>
</FormGroup>
</Grid>
)}
</Grid>
</Grid>
</Grid>
</MainCard>
</Grid>
{/* ================================================= || Drop || ================================================= */}
<Grid item xs={12} sm={6}>
<MainCard sx={{ height: '100%' }}>
<Grid container spacing={2} sx={{ height: '100%' }}>
<Grid item xs={12} sx={{ height: '100%' }}>
<Stack direction={'row'} justifyContent={'space-between'} alignItems={'center'}>
<Typography variant="h5" sx={{ mb: 0 }}>
Drop Details
</Typography>
<Stack direction={'row'} gap={1} alignItems={'center'}>
{dropCust?.firstname && (
<Typography variant="caption" sx={{ color: 'success.main', fontWeight: 600 }}>
Selected
</Typography>
)}
<Button
variant={dropSearchOpen ? 'outlined' : 'contained'}
disabled={!locationid}
size="small"
onClick={() => {
if (!appId && !tenantid && !locationid) {
OpenToast('Please select Location, Tenant and Business!', 'warning', 3000);
} else if (!appId && !tenantid) {
OpenToast('Please select Location and Tenant!', 'warning', 3000);
} else if (!appId) {
OpenToast('Please select Location!', 'warning', 3000);
} else {
setDropSearchOpen((prev) => !prev);
setPickupSearchOpen(false);
setSearchCustList('');
setpickordrop(2);
}
}}
sx={{ minWidth: 100 }}
>
{dropSearchOpen ? 'Close' : 'Select Drop'}
</Button>
</Stack>
</Stack>
{/* ============ Inline Drop Search Panel ============ */}
<div
style={{
overflow: 'hidden',
maxHeight: dropSearchOpen ? '380px' : '0px',
transition: 'max-height 0.35s cubic-bezier(0.4, 0, 0.2, 1)',
marginTop: dropSearchOpen ? '12px' : '0px'
}}
>
<div
style={{
border: '1.5px solid #e2e8f0',
borderRadius: '12px',
background: '#f8fafc',
padding: '12px',
display: 'flex',
flexDirection: 'column',
gap: '8px'
}}
>
{/* Search Input */}
<OutlinedInput
autoFocus={dropSearchOpen}
fullWidth
size="small"
placeholder="Search customer..."
value={dropSearchOpen ? searchCustList : ''}
onChange={(e) => setSearchCustList(e.target.value)}
sx={{ bgcolor: 'white', borderRadius: '8px' }}
startAdornment={
<InputAdornment position="start">
<SearchOutlined style={{ fontSize: 'small' }} />
</InputAdornment>
}
endAdornment={
searchCustList ? (
<IconButton size="small" onClick={() => setSearchCustList('')}>
<ClearIcon fontSize="small" />
</IconButton>
) : null
}
/>
{/* Customer List */}
<div
style={{
maxHeight: '280px',
overflowY: 'auto',
display: 'flex',
flexDirection: 'column',
gap: '6px'
}}
>
{customerlist?.length === 0 ? (
<Stack alignItems="center" justifyContent="center" sx={{ py: 2 }}>
<Empty />
</Stack>
) : (
customerlist?.map((address, index) => (
<Button
key={index}
disabled={pickCust?.customerid === address.customerid}
onClick={() => {
setDropSearchOpen(false);
setAddId2(1);
setEndPoint({ latitude: address.latitude, longitude: address.longitude });
setDropCust(address);
setdropNum(address.contactno);
setSearchCustList('');
}}
sx={{
width: '100%',
border: '1.5px solid',
borderColor: dropCust?.customerid === address.customerid ? 'primary.main' : 'grey.200',
borderRadius: '8px',
p: 1.25,
textAlign: 'left',
justifyContent: 'flex-start',
bgcolor: dropCust?.customerid === address.customerid ? 'primary.lighter' : 'white',
'&:hover': { borderColor: 'primary.main', bgcolor: 'rgba(24,144,255,0.04)' }
}}
>
<div style={{ width: '100%' }}>
<Typography variant="subtitle2" sx={{ textAlign: 'left', fontWeight: 600 }}>
{`${address.firstname} (${address.contactno})`}
</Typography>
<Typography variant="caption" color="secondary" sx={{ textAlign: 'left', display: 'block' }}>
{address.address}
</Typography>
</div>
</Button>
))
)}
</div>
</div>
</div>
</Grid>
<Grid item xs={12}>
<Grid container spacing={4}>
{/* ====================================== ||Contact Name (drop) || ====================================== */}
<Grid item xs={12} sm={6}>
<TextField
inputRef={textFieldRef2}
// disabled={!appId || !tenantid || !locationid}
fullWidth
variant="outlined"
label="Contact Name"
InputProps={{
startAdornment: (
<IconButton>
<FaUser />
</IconButton>
)
}}
value={dropCust?.firstname || ''}
onChange={(e) => {
setDropCust({ ...dropCust, firstname: e.target.value });
}}
/>
</Grid>
{/* ====================================== ||Contact Number (drop) || ====================================== */}
<Grid item xs={12} sm={6}>
<TextField
error={numErr2}
// disabled={!appId || !tenantid || !locationid}
fullWidth
type="number"
variant="outlined"
label="Contact Number"
InputProps={{
startAdornment: (
<IconButton>
<FaPhoneAlt color={numErr2 && 'red'} />
</IconButton>
)
}}
value={dropCust?.contactno || ''}
onChange={(e) => {
if (e.target.value.length <= 10) {
setDropCust({ ...dropCust, contactno: e.target.value });
}
if (dropNum == e.target.value) {
setShowCheck2(0);
} else {
setShowCheck2(1);
}
if (e.target.value.length < 10) {
setNumErr2(true);
} else {
setNumErr2(false);
}
}}
sx={{ color: numErr2 ? 'red' : 'inherit' }}
/>
</Grid>
{/* ====================================== || Address (drop) || ====================================== */}
<Grid item xs={12}>
<Stack spacing={1.25} sx={{ mt: 0 }}>
{addId2 == 0 ? (
<div>
<TextField
// disabled={!appId || !tenantid || !locationid}
id="addressAuto2"
label="Address "
variant="outlined"
fullWidth
value={inputValue2 || ''}
onChange={(e) => setInputValue2(e.target.value)}
InputProps={{
endAdornment: (
<IconButton
onClick={() => {
setInputValue2('');
setDropCust({
...dropCust,
doorno: '',
suburb: '',
city: '',
postcode: '',
landmark: ''
});
setShowDistance(false);
setEndPoint({ latitude: 0, longitude: 0 });
}}
size="small"
>
<CloseIcon />
</IconButton>
)
}}
/>
</div>
) : (
<TextField
variant="outlined"
InputProps={{
endAdornment: (
<IconButton
onClick={() => {
setAddId2(0);
setDropCust({
...dropCust,
// firstname: '',
// contactno: '',
doorno: '',
suburb: '',
city: '',
postcode: '',
landmark: ''
});
setShowDistance(false);
setEndPoint({ latitude: 0, longitude: 0 });
}}
>
<ClearIcon />
</IconButton>
)
}}
value={dropCust?.address || ''}
onChange={(e) => {
setPickCust({ ...dropCust, address: e.target.value });
if (e.target.value == '') {
setAddId2(0);
setShowDistance(false);
setEndPoint({ latitude: 0, longitude: 0 });
}
}}
/>
)}
</Stack>
</Grid>
{/* ====================================== ||Door No (drop) || ====================================== */}
<Grid item xs={12} sm={6}>
<TextField
// disabled={!appId || !tenantid || !locationid}
fullWidth
variant="outlined"
label="Door No / Street"
InputProps={{
startAdornment: (
<IconButton>
<GiDoorHandle />
</IconButton>
)
}}
value={dropCust?.doorno || ''}
onChange={(e) => {
setDropCust({ ...dropCust, doorno: e.target.value });
}}
/>
</Grid>
{/* ====================================== ||Suburb (drop) || ====================================== */}
<Grid item xs={12} sm={6}>
<TextField
// disabled={!appId || !tenantid || !locationid}
fullWidth
variant="outlined"
label="Location"
InputProps={{
startAdornment: (
<IconButton>
<FaLocationDot />
</IconButton>
)
}}
value={dropCust?.suburb || ''}
onChange={(e) => {
setDropCust({ ...dropCust, suburb: e.target.value });
}}
/>
</Grid>
{/* ====================================== ||Postcode (drop) || ====================================== */}
<Grid item xs={12} sm={6}>
<TextField
// disabled={!appId || !tenantid || !locationid}
fullWidth
variant="outlined"
label="Postcode"
InputProps={{
startAdornment: (
<IconButton>
<TbMapPinCode />
</IconButton>
)
}}
value={dropCust?.postcode || ''}
onChange={(e) => {
setDropCust({ ...dropCust, postcode: e.target.value });
}}
/>
</Grid>
{/* ====================================== ||Landmark (drop) || ====================================== */}
<Grid item xs={12} sm={6}>
<TextField
// disabled={!appId || !tenantid || !locationid}
fullWidth
variant="outlined"
label="Landmark"
InputProps={{
startAdornment: (
<IconButton>
<FaLandmarkDome />
</IconButton>
)
}}
value={dropCust?.landmark || ''}
onChange={(e) => {
setDropCust({ ...dropCust, landmark: e.target.value });
}}
/>
</Grid>
{/* ====================================== ||Checkbox save for later (drop) || ====================================== */}
{showCheck2 == 1 && (
<Grid item xs={12} sx={{ display: 'flex', justifyContent: 'end' }}>
<FormGroup>
<FormControlLabel
control={
<Checkbox
checked={isNumChange2 === 1}
onChange={(e) => {
setIsNumChange2(e.target.checked ? 1 : 0);
}}
/>
}
label="Save For Later"
/>
</FormGroup>
</Grid>
)}
</Grid>
</Grid>
</Grid>
</MainCard>
</Grid>
</Grid>
</Grid>
{/* ================================================= || Live Map Preview || ================================================= */}
<Grid item xs={12} md={4}>
<MainCard sx={{ height: '100%', minHeight: '400px', display: 'flex', flexDirection: 'column' }}>
<Typography variant="h5" sx={{ mb: 2 }}>
Live Route Preview
</Typography>
<div style={{ flex: 1, minHeight: '380px', position: 'relative', borderRadius: '8px' }}>
<OrderMap startPoint={startPoint} endPoint={endPoint} appLocaLat={appLocaLat} appLocaLng={appLocaLng} />
</div>
</MainCard>
</Grid>
</Grid>
</Grid>
{/* ================================================= || Category || ================================================= */}
<Grid item xs={12} sx={{}}>
<Stack sx={{ width: '100%', my: 2 }} direction={'row'} alignItems={'center'} gap={2} justifyContent={'start'}>
<Avatar size="small" sx={{ bgcolor: 'primary.main', width: 34, height: 34 }}>
3
</Avatar>
<Typography variant="h4">Package Details</Typography>
</Stack>
<MainCard sx={{}}>
<Grid container spacing={2}>
<Grid item xs={12} sm={4}>
<Stack>
<Autocomplete
id="combo-box-demo"
options={subCat}
getOptionLabel={(option) => `${option.subcategoryname}` || ''}
fullWidth
renderInput={(params) => <TextField {...params} label={'Category'} />}
onChange={(event, value, reason) => {
if (value) {
console.log(value);
setSubCatName(value.subcategoryname || '');
setSubCatId(value.subcategoryid || 0);
} else if (reason) {
setSubCatName(null);
setSubCatId(null);
}
}}
/>
</Stack>
</Grid>
<Grid item xs={12} sm={4}>
<Stack>
<TextField
type="number"
value={collectionamt}
fullWidth
label={'Cash Collect'}
onChange={(e) => {
setCollectionamt(e.target.value);
}}
inputProps={{ min: 0 }}
/>
</Stack>
</Grid>
<Grid item xs={12} sm={4}>
<Stack>
<TextField
type="number"
value={quantity}
fullWidth
label={'Quantity'}
onChange={(e) => {
setQuantity(e.target.value);
}}
inputProps={{ min: 1 }}
/>
</Stack>
</Grid>
</Grid>
</MainCard>
</Grid>
<Grid item xs={12} sx={{}}>
<Stack sx={{ width: '100%', my: 2 }} direction={'row'} alignItems={'center'} gap={2} justifyContent={'start'}>
<Avatar size="small" sx={{ bgcolor: 'primary.main', width: 34, height: 34 }}>
4
</Avatar>
<Typography variant="h4">Schedule & Delivery Options</Typography>
</Stack>
</Grid>
{/* ================================================= || Time || ================================================= */}
<Fragment>
<Grid item xs={12} sm={6}>
<MainCard sx={{ height: 'auto' }}>
<Grid container>
<Grid item xs={12}>
{/* <Stack direction="row" alignItems="center" gap={0.5} sx={{ width: '100%', my: 2 }}>
<IconButton
size="small"
sx={{
color: 'primary.main',
width: 34,
height: 34
}}
>
<AccessTimeIcon sx={{ fontSize: 18 }} />
</IconButton>
<Typography variant="h4">Schedule</Typography>
</Stack> */}
<Typography variant="h5">Pickup Slots </Typography>
<Typography variant="h5"> Date</Typography>
<LocalizationProvider dateAdapter={AdapterDayjs} sx={{}}>
<DatePicker
format="DD-MM-YYYY"
onChange={(e) => {
let dateres11 = dayjs().diff(dayjs(`${dayjs(e).format('YYYY-MM-DD')}`), 'd');
console.log('dateres11');
console.log(dateres11);
setSelectedtime('');
if (dateres11 <= 0) {
console.log('startdate', e);
setStartdate(e);
let arr = [];
timeslotarr.map((val) => {
if (
dayjs().diff(dayjs(`${dayjs(e).format('MM-DD-YYYY')} ${dayjs(val).format('HH:mm:ss')}`), 'm') <= 0
) {
arr.push(val);
}
});
} else {
setAlertmessage('choose Upcoming Date');
opentoast('choose Upcoming Date', 'warning');
setStartdate(NaN);
}
}}
value={dayjs(startdate)}
sx={{ width: '100%', mt: 2 }}
disablePast
/>
</LocalizationProvider>
<Typography variant="h5" sx={{ my: 2 }}>
Time
</Typography>
<MainCard>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<Stack direction="row" spacing={2} sx={{ width: '100%' }}>
{/* ================= PICKUP ================= */}
<Box
sx={{
position: 'relative',
flex: 1,
border: '2px dashed',
borderColor: theme.palette.primary.light,
borderRadius: 4,
p: 3,
cursor: 'pointer',
backgroundColor: theme.palette.primary.lighter
}}
onClick={() => setPickDialog(true)}
>
<TimePicker
open={pickDialog}
onClose={() => setPickDialog(false)}
onAccept={() => setPickDialog(false)} // ✅ CLOSE HERE
value={pickupdate}
onChange={handlePickupChange}
disablePast
minutesStep={5}
slotProps={{
textField: {
sx: {
position: 'absolute',
inset: 0,
opacity: 0
}
}
}}
/>
<Box display="flex" alignItems="center" gap={1}>
<Box
sx={{
backgroundColor: theme.palette.primary[100],
borderRadius: 2,
p: 0.8,
color: theme.palette.primary.main
}}
>
<ArrowUpOutlined sx={{ fontSize: 14 }} />
</Box>
<Typography variant="h6" color="text.secondary">
Pickup Time
</Typography>
</Box>
<Typography
variant="h3"
sx={{
fontWeight: 700,
color: theme.palette.primary.main,
mt: 2
}}
>
{pickupdate.format('hh:mm A')}
</Typography>
</Box>
{/* ================= DELIVERY ================= */}
<Box
sx={{
position: 'relative',
flex: 1,
border: '2px dashed',
borderColor: theme.palette.success.light,
borderRadius: 4,
p: 3,
cursor: 'pointer',
backgroundColor: theme.palette.success.lighter
}}
onClick={() => setDeliveryDialog(true)}
>
<TimePicker
open={deliveryDialog}
onClose={() => setDeliveryDialog(false)}
onAccept={() => setDeliveryDialog(false)} // ✅ CLOSE HERE
value={deliverydate}
onChange={(newValue) => {
if (!newValue) return;
// Prevent delivery before pickup
if (newValue.isBefore(pickupdate)) {
setDeliverydate(pickupdate.add(30, 'minute'));
} else {
setDeliverydate(newValue);
}
setDeliveryDialog(false);
}}
minTime={pickupdate}
disablePast
minutesStep={5}
slotProps={{
textField: {
sx: {
position: 'absolute',
inset: 0,
opacity: 0
}
}
}}
/>
<Box display="flex" alignItems="center" gap={1}>
<Box
sx={{
backgroundColor: theme.palette.success[100],
borderRadius: 2,
p: 0.8,
color: theme.palette.success.main
}}
>
<ArrowDownOutlined sx={{ fontSize: 14 }} />
</Box>
<Typography variant="h6" color="text.secondary">
Delivery Time
</Typography>
</Box>
<Typography
variant="h3"
sx={{
fontWeight: 700,
color: theme.palette.success.main,
mt: 2
}}
>
{deliverydate.format('hh:mm A')}
</Typography>
</Box>
</Stack>
</LocalizationProvider>
</MainCard>
{/* <MainCard
sx={{
height: '210px',
mt: 1,
overflowY: 'scroll',
'&::-webkit-scrollbar': {
width: '3px', // Width of vertical scrollbar
height: '10px' // Height of horizontal scrollbar
},
'&::-webkit-scrollbar-thumb': {
backgroundColor: '#65387A' // Color of the scrollbar thumb
}
}}
>
<Grid container spacing={2}>
{timeslotarr.map((val, index) => {
if (
dayjs().diff(dayjs(`${dayjs(startdate).format('MM-DD-YYYY')} ${dayjs(val).format('HH:mm:ss')}`), 'm') <= 0
) {
return (
<Fragment key={index}>
<Grid item>
<Chip
key={index}
sx={{ cursor: 'pointer', width: 90 }}
color={dayjs(selectedtime).format('HH:mm') == dayjs(val).format('HH:mm') ? 'primary' : 'default'}
label={dayjs(val).format('hh:mm A')}
onClick={() => {
if (distance > appLocaRadius) {
setOpen(true);
} else if (showDistance) {
console.log('selectedtime', val);
setSelectedtime(val);
} else {
opentoast('Out of city limit', 'error');
}
}}
/>
</Grid>
</Fragment>
);
}
})}
</Grid>
</MainCard> */}
</Grid>
</Grid>
</MainCard>
</Grid>
<Grid item xs={12} sm={6}>
<MainCard sx={{ height: '100%' }}>
{showDistance && (
<Stack sx={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between', mb: 3 }}>
<Stack spacing={1.5}>
<Chip label={`Distance: ${distance} km`} size="medium" variant="contained" color="primary" />
</Stack>
<Stack spacing={1.5}>
<Chip label={`Charge: ₹${totalCharge.toFixed(2)}`} size="medium" variant="contained" color="primary" />
</Stack>
</Stack>
)}
<Typography variant="h5">Weight</Typography>
<Stack direction={'row'} justifyContent={'space-evenly'} sx={{ border: '1px solid #eee', my: 2, py: 2 }}>
<Chip
variant="contained"
color="default"
label="1-10kgs"
sx={chipStyle('1-10kgs')}
onClick={() => {
handleChipClick('1-10kgs');
setWeight('1-10kgs');
}}
/>
<Chip
variant="contained"
color="default"
label="11-20kgs"
sx={chipStyle('11-20kgs')}
onClick={() => {
handleChipClick('11-20kgs');
setWeight('11-20kgs');
}}
/>
<Chip
variant="contained"
color="default"
label="21-30kgs"
sx={chipStyle('21-30kgs')}
onClick={() => {
handleChipClick('21-30kgs');
setWeight('21-30kgs');
}}
/>
</Stack>
<Stack direction={'row'} justifyContent={'space-between'} alignItems={'center'} sx={{}}>
<Typography variant="h5" noWrap>
SMS Delivery
</Typography>
<Switch
checked={isSms === 1}
onChange={(e) => {
setIsSms(e.target.checked ? 1 : 0);
}}
/>
</Stack>
<Stack sx={{ mt: 3 }} gap={1}>
<Typography variant="h5" noWrap>
Notes
</Typography>
<TextField
focused
id="outlined-multiline-static"
sx={{ width: '100%', height: '100%', mb: 2 }}
multiline
rows={1}
placeholder="Add any special instructions or notes for the delivery"
value={otherinstructions}
onChange={(e) => setOtherinstructions(e.target.value)}
/>
</Stack>
</MainCard>
</Grid>
</Fragment>
</Grid>
</Grid>
<Grid item xs={12}>
{/* ================================================= || Notes || ================================================= */}
<MainCard sx={{ mt: 2 }}>
<Grid container>
<Grid item xs={12}>
{/* </Grid> */}
</Grid>
<Stack direction="row" justifyContent={'end'} sx={{ mt: 2, width: '100%' }}>
<Button
size="medium"
disabled={!showDistance || !selectedtime}
variant="outlined"
onClick={() => {
setLoading(true);
setBtnLoading(true);
createsubmitobj2();
setTimeout(() => {
setLoading(false);
setBtnLoading(false);
}, 2000);
}}
sx={{
'&:hover': {
transform: 'scale(1.05)',
transition: 'transform 0.3s ease'
}
}}
>
{btnLoading ? <CircularProgress color="primary" size={20} thickness={10} /> : 'Create'}
</Button>
</Stack>
</Grid>
</MainCard>
</Grid>
</Grid>
{/* </Box> */}
</Grid>
{/* Customer Dialog removed — replaced with inline search panels above */}
{/* ============================================= || location error Dialog || ============================================= */}
<Dialog
open={open}
onClose={() => {
setOpen(false);
}}
>
<DialogTitle sx={{ textAlign: 'center' }}>
<HighlightOffIcon sx={{ fontSize: 60, color: theme.palette.error.main }} />
<Typography variant="h3" sx={{ color: theme.palette.error.main }}>
Error
</Typography>
</DialogTitle>
<DialogContent>
<Typography variant="h4" color={'secondary'}>
Service not available at this location
</Typography>
</DialogContent>
<DialogActions
sx={{
bgcolor: theme.palette.error.main,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: 20,
color: 'white',
cursor: 'pointer'
}}
onClick={() => {
setOpen(false);
}}
>
Close
</DialogActions>
</Dialog>
</Grid>
</>
);
};
export default Createorder1;