initial commit

This commit is contained in:
2026-05-13 17:48:36 +05:30
commit 5a80256856
305 changed files with 80994 additions and 0 deletions

600
src/pages/api/api.js Normal file
View File

@@ -0,0 +1,600 @@
import axios from 'axios';
import { OpenToast } from 'components/third-party/OpenToast';
import dayjs from 'dayjs';
const userid = localStorage.getItem('userid');
// ==============================|| fetchAppLocations||============================== //
export const fetchAppLocations = async () => {
const response = await axios.get(`${process.env.REACT_APP_URL}/partners/getlocations/?userid=${userid}`);
const updatedLocations = [
...response.data.details,
{ locationname: 'All', applocationid: 0 } // Add your new object here
];
return updatedLocations;
};
// ==============================|| fetchPercentageData (orders) ||============================== //
export const fetchPercentageData = async (appId) => {
const response = await axios.get(`${process.env.REACT_APP_URL}/orders/getordersummary/?applocationid=${appId}`);
const details = response.data.details;
return {
created: details.created.toString(),
uncoveredOrders: details.pending.toString(),
coveredOrders: details.delivered.toString(),
cancelled: details.cancelled.toString(),
percentage1: (Math.round((details.created / details.total) * 100) || 0).toString(),
percentage2: (Math.round((details.pending / details.total) * 100) || 0).toString(),
percentage3: (Math.round((details.delivered / details.total) * 100) || 0).toString(),
percentage4: (Math.round((details.cancelled / details.total) * 100) || 0).toString()
};
};
// ===================================================== || getTenants || =====================================================
export const getTenants = async (appId) => {
const response = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenants/?applocationid=${appId}&status=active`);
if (response.data.status) {
let arr = [];
response.data.details.map((val) => {
arr.push({
...val,
label: `${val.tenantname}`
});
});
return arr;
}
};
// ============================================= || gettenantlocations (branches) || =============================================
export const gettenantlocations = async (appId) => {
try {
const res = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenantlocations/?tenantid=${appId}`);
return res.data.details;
} catch (err) {
console.log('gettenantlocations', err);
}
};
// ==============================|| fetchorderscount (orders) ||============================== //
export const fetchorderscount = async ({ queryKey }) => {
const [, appId, startdate, enddate, currentStatus, tenantid, locationid] = queryKey;
const url = `${process.env.REACT_APP_URL}/orders/getordersummary/?applocationid=${appId}&tenantid=${tenantid}&locationid=${locationid}&fromdate=${startdate}&todate=${enddate}&status=${currentStatus}`;
const response = await axios.get(url);
return response.data.details;
};
// ==============================|| fetchOrders (orders) ||============================== //
// export const fetchOrders = async ({ queryKey }) => {
// const [, appId, currentStatus, searchword, startdate, enddate, page, rowsPerPage, tenantid, locationid] = queryKey;
// const url = `${
// process.env.REACT_APP_URL
// }/orders/getorders/?applocationid=${appId}&tenantid=${tenantid}&locationid=${locationid}&status=${currentStatus}&fromdate=${startdate}&todate=${enddate}&pageno=${
// page + 1
// }&pagesize=${rowsPerPage}&keyword=${searchword}`;
// const response = await axios.get(url);
// return response.data.details.map((val, i) => ({ ...val, sno: i + 1 }));
// };
export const fetchOrders = async ({ pageParam = 1, queryKey }) => {
const [, appId, currentStatus, debouncedSearch, startdate, enddate, rowsPerPage, tenantid, locationid] = queryKey;
const url = `${process.env.REACT_APP_URL}/orders/tenant/getorders/?applocationid=${appId}&tenantid=${tenantid}&locationid=${locationid}&status=${currentStatus}&fromdate=${startdate}&todate=${enddate}&keyword=${debouncedSearch}&pageno=${pageParam}&pagesize=${rowsPerPage}`;
const response = await axios.get(url);
return {
rows: response.data.details,
nextPage: response.data.details.length === Number(rowsPerPage) ? pageParam + 1 : undefined
};
};
// ==============================|| fetchPaymentType (orders) ||============================== //
export const fetchPaymentType = async () => {
const { data } = await axios.get(`${process.env.REACT_APP_URL}/utils/getapptypes/?tag=paymentmode`);
return data.details.map((val) => ({
...val,
label: val.typename
}));
};
// ==============================|| fetchRidersList (orders) ||============================== //
export const fetchRidersList = async ({ queryKey }) => {
try {
const [, appId] = queryKey; // Extract appId from queryKey
const { data } = await axios.get(`${process.env.REACT_APP_URL}/partners/getriders/?applocationid=${appId}`);
console.log('data', data);
const response = data?.details
? data?.details.map((val) => ({
...val,
label: `${val.firstname} ${val.lastname} | ${val.contactno}`
}))
: [];
return response;
} catch (err) {
OpenToast(err.message, 'error', 2000);
throw err; // 🔥 REQUIRED
}
};
// ==============================|| createOptimisationDeliveries (orders) Arrange the order ||============================== //
export const createOptimisationDeliveries = async (deliveryData) => {
// optimse the orders
const response = await axios.post(`https://routes.workolik.com/api/v1/optimization/createdeliveries`, deliveryData.deliveries);
return response.data;
};
// ==============================|| finalCreatedeliveries (orders) ||============================== //
export const finalCreatedeliveries = async (deliveryData) => {
console.log('deliveryData', deliveryData.deliveries);
const response = await axios.post(`https://jupiter.nearle.app/live/api/v1/deliveries/createdeliveries`, deliveryData.deliveries);
return response.data;
};
// ==============================|| createAutomationDeliveries (orders) Auto rider Assign ||============================== //
export const createAutomationDeliveries = async (variables) => {
console.log('variables', variables);
// optimse the orders and auto rider assign
const response = await axios.post(
variables.selectedMode.value == 1
? `https://routes.workolik.com/api/v1/optimization/riderassign?hypertuning_params=${variables.hypertuning_params}`
: `https://routemate.workolik.com/api/v1/optimization/riderassign?strategy=multi_trip`,
variables.selectedMode.value == 1 ? variables.deliveries : variables.data
);
return response.data;
};
// ==============================|| notifyRider (orders / deliveries) ||============================== //
export const notifyRider = async (riderToken) => {
if (!riderToken) {
throw new Error('Invalid rider token');
}
console.log('notify rider called');
console.log('riderToken', riderToken);
const response = await axios.post(`${process.env.REACT_APP_URL}/utils/notifyuser`, {
token: riderToken,
notification: {
title: 'NearleXpress',
body: 'Orders have been placed for delivery. Kindly accept and process deliveries',
sound: 'ring',
image: ''
}
});
return response.data;
};
// ==============================|| cancelOrder (orders) ||============================== //
export const cancelOrder = async (orderheaderid) => {
const response = await axios.put(`${process.env.REACT_APP_URL}/orders/updateorder`, {
orderheaderid: orderheaderid,
orderstatus: 'cancelled',
cancelled: dayjs().format('YYYY-MM-DD HH:mm:ss')
});
return response.data;
};
// ==============================|| cancelMultipleOrder (orders) ||============================== //
export const cancelMultipleOrder = async (orderlist) => {
console.log('data', orderlist);
const data = orderlist?.map((e) => ({
orderheaderid: e.orderheaderid,
orderstatus: 'cancelled',
cancelled: dayjs().format('YYYY-MM-DD HH:mm:ss')
}));
// Send request if needed
const response = await axios.put(`${process.env.REACT_APP_URL}/orders/updatemultipleorders`, data);
return response.data;
};
// ==============================|| fetchDeliveries (deliveries) ||============================== //
export const fetchDeliveries = async ({ pageParam = 1, queryKey }) => {
let [, appId, userid, currentStatus, startdate, enddate, rowsPerPage, searchword, tenantid, locationid, riderid] = queryKey;
currentStatus = currentStatus == 'All' ? 'all' : currentStatus;
const url =
appId === 0
? `${process.env.REACT_APP_URL}/deliveries/getdeliveries/?appuserid=${userid}&status=${currentStatus}&fromdate=${startdate}&todate=${enddate}&pageno=${pageParam}&pagesize=${rowsPerPage}&keyword=${searchword}&tenantid=${tenantid}&locationid=${locationid}&userid=${riderid}`
: `${process.env.REACT_APP_URL}/deliveries/getdeliveries/?applocationid=${appId}&status=${currentStatus}&fromdate=${startdate}&todate=${enddate}&pageno=${pageParam}&pagesize=${rowsPerPage}&keyword=${searchword}&tenantid=${tenantid}&locationid=${locationid}&userid=${riderid}`;
const response = await axios.get(url);
return {
rows: response.data.details,
nextPage: response.data.details.length === Number(rowsPerPage) ? pageParam + 1 : undefined
};
};
// ==============================|| fetchPercentageAPI (deliveries) ||============================== //
export const fetchPercentageAPI = async (appId) => {
const url = `${process.env.REACT_APP_URL}/deliveries/deliverysummary/?applocationid=${appId}`;
const response = await axios.get(url);
const data = response.data.details;
return {
coveredOrders: data.delivered.toString(),
cancelledOrders: data.cancelled.toString(),
uncoveredOrders: data.pending.toString(),
assignedOrders: data.accepted.toString(),
createdOrders: data.created.toString(),
closedOrders: data.delivered.toString(),
pickedOrders: data.picked.toString(),
percentage1: (Math.round((data.pending / data.total) * 100) || 0).toString(),
percentage2: (Math.round((data.accepted / data.total) * 100) || 0).toString(),
percentage3: (Math.round((data.picked / data.total) * 100) || 0).toString(),
percentage4: (Math.round((data.delivered / data.total) * 100) || 0).toString()
};
};
// ==============================|| fetchCountAPI (deliveries) ||============================== //
export const fetchCountAPI = async (appId, userid, startdate, enddate, rowsPerPage, debouncedSearch, tenantid, locationid, riderid) => {
const url =
appId == 0
? `${process.env.REACT_APP_URL}/deliveries/deliverysummary/?appuserid=${userid}&fromdate=${startdate}&todate=${enddate}`
: `${process.env.REACT_APP_URL}/deliveries/deliverysummary/?applocationid=${appId}&fromdate=${startdate}&todate=${enddate}&tenantid=${tenantid}&locationid=${locationid}&userid=${riderid}`;
const response = await axios.get(url);
const data = response.data.details;
return {
uncoveredLength: data.pending,
assignedLength: data.accepted,
arrivedLength: data.arrived,
pickedLength: data.picked,
activeLength: data.active,
coveredLength: data.delivered,
cancelLength: data.cancelled,
skippedLength: data.skipped
};
};
// ==============================|| cancelDeliveryAPI (deliveries) ||============================== //
export const cancelDeliveryAPI = async (selectedRow, cancelFeed) => {
const payload = {
deliveryid: selectedRow.deliveryid,
orderheaderid: selectedRow.orderheaderid,
orderstatus: 'cancelled',
canceltime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
feedback: cancelFeed
};
const response = await axios.put(`${process.env.REACT_APP_URL}/deliveries/updatedelivery`, payload);
return response.data;
};
// ==============================|| getorderdetails (deliveries) ||============================== //
export const getorderdetails = async (orderHeaderid) => {
const response = await axios.get(`${process.env.REACT_APP_URL}/orders/getorderdetails?orderheaderid=${orderHeaderid}`);
return response.data;
};
// ==============================|| changeRiderAPI (deliveries) ||============================== //
export const changeRiderAPI = async (selectedRider, selectedRow) => {
console.log('selectedRider', selectedRider);
console.log('selectedRow', selectedRow);
return axios.put(`${process.env.REACT_APP_URL}/deliveries/updatedelivery`, {
userid: selectedRider.userid,
deliveryid: selectedRow.deliveryid,
orderheaderid: selectedRow.orderheaderid,
orderstatus: 'pending',
assigntime: dayjs().format('YYYY-MM-DD HH:mm:ss')
});
};
// ==============================|| updateDeliveryAPI (deliveries) ||============================== //
export const updateDeliveryAPI = async (orderData) => {
return axios.put(`${process.env.REACT_APP_URL}/deliveries/updatedelivery`, orderData);
};
// ==============================|| getalltenants (tenants) ||============================== //
export const getalltenants = async ({ queryKey }) => {
const [, appId, debouncedSearch, status, page, rowsPerPage] = queryKey;
try {
let url = `${process.env.REACT_APP_URL
}/tenants/getalltenants/?status=${status}&applocationid=${appId}&keyword=${debouncedSearch}&pageno=${page + 1
}&pagesize=${rowsPerPage}&moduleid=6`;
const response = await axios.get(url);
return response.data.details; // return only data, keep it clean
} catch (err) {
const message = err.response?.data?.message || err.message || 'Something went wrong';
OpenToast(message);
return null; // return null for failure
}
};
// ==============================|| gettenantsummary (tenants) ||============================== //
export const gettenantsummary = async ({ queryKey }) => {
const [, appId] = queryKey;
try {
const response = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenantsummary/?moduleid=6&applocationid=${appId}`);
return response.data.summary; // return only data, keep it clean
} catch (err) {
const message = err.response?.data?.message || err.message || 'Something went wrong';
OpenToast(message);
return null; // return null for failure
}
};
// ==============================|| getpricinglist (tenants) ||============================== //
export const getpricinglist = async ({ queryKey }) => {
const [, appId] = queryKey;
try {
const response = await axios.get(`${process.env.REACT_APP_URL}/tenants/getpricinglist/?moduleid=6&applocationid=${appId}`);
return response.data.summary; // return only data, keep it clean
} catch (err) {
const message = err.response?.data?.message || err.message || 'Something went wrong';
OpenToast(message);
return null; // return null for failure
}
};
// ==============================|| getcustomersummary (customers) ||============================== //
export const getcustomersummary = async ({ queryKey }) => {
const [, appId] = queryKey;
try {
const response = await axios.get(`${process.env.REACT_APP_URL}/customers/getcustomersummary?applocationid=${appId}`);
return response.data.summary;
} catch (err) {
const message = err.response?.data?.message || err.message || 'Something went wrong';
OpenToast(message);
return null; // return null for failure
}
};
// ==============================|| getallcustomers (customers) ||============================== //
export const getallcustomers = async ({ pageParam = 1, queryKey }) => {
const [, appId, debouncedSearch, rowsPerPage] = queryKey;
try {
const response = await axios.get(`${process.env.REACT_APP_URL}/customers/getallcustomers/`, {
params: {
applocationid: appId,
keyword: debouncedSearch,
pageno: pageParam,
pagesize: rowsPerPage
}
});
return {
data: response.data.details || [],
nextPage: response.data.details?.length === rowsPerPage ? pageParam + 1 : undefined
};
} catch (err) {
const message = err.response?.data?.message || err.message || 'Something went wrong';
OpenToast(message);
throw err; // IMPORTANT for React Query
}
};
// ==============================|| fetchAllRiders (riders) ||============================== //
export const fetchAllRiders = async ({ pageParam = 1, queryKey }) => {
try {
const [, appId, debouncedSearch, tabvalue] = queryKey;
const url = `${process.env.REACT_APP_URL
}/partners/getallriders/?applocationid=${appId}&pageno=${pageParam}&pagesize=${20}&keyword=${debouncedSearch}&status=${tabvalue == 0 ? '' : 'Active'
}`;
const res = await axios.get(url);
return {
details: res.data.details,
nextPage: res.data.details.length === 20 ? pageParam + 1 : undefined
};
} catch (err) {
console.log('fetchAllRiders err', err.message);
return [];
}
};
// ==============================|| getallridersummary (riders) ||============================== //
export const getallridersummary = async ({ queryKey }) => {
try {
const [, appId, tabvalue] = queryKey;
const response = await axios.get(
`${process.env.REACT_APP_URL}/partners/getallridersummary/?applocationid=${appId}&status=${tabvalue == 0 ? '' : 'Active'}`
);
return response.data.details;
} catch (err) {
console.log('getallridersummary err', err.message);
return [];
}
};
// ==============================|| fetchRiders (riders), active riders ||============================== //
export const fetchRiders = async ({ pageParam = 1, queryKey }) => {
try {
const [, appId, debouncedSearch] = queryKey;
const url = `${process.env.REACT_APP_URL
}/partners/getriders/?applocationid=${appId}&pageno=${pageParam}&pagesize=${20}&keyword=${debouncedSearch}`;
const res = await axios.get(url);
return {
details: res.data.details,
nextPage: res.data.details.length === 20 ? pageParam + 1 : undefined
};
} catch (err) {
console.log('fetchRiders err', err.message);
return [];
}
};
// ==============================|| getriderstatus (riders)||============================== //
export const getriderstatus = async () => {
const response = await axios.get(`${process.env.REACT_APP_URL}/utils/getriderstatus`);
return response.data.data;
};
// ==============================|| getreportsummary (orders summary)||============================== //
export const getreportsummary = async ({ queryKey }) => {
console.log('queryKey for getreportsummary', queryKey);
const [appId, tenantid, locationid, startdate, enddate] = queryKey;
const response = await axios.get(
`${process.env.REACT_APP_URL}/deliveries/getreportsummary/?applocationid=${appId}&tenantid=${tenantid}&locationid=${locationid}&fromdate=${startdate}&todate=${enddate}`
);
console.log('getreportsummary', response.data.details);
return response.data.details;
};
// ==============================|| getreportlocationsummary (orders summary)||============================== //
export const getreportlocationsummary = async ({ queryKey }) => {
console.log('queryKey for getreportlocationsummary', queryKey);
const [appId, tenantid, locationid, startdate, enddate] = queryKey;
const response = await axios.get(
`${process.env.REACT_APP_URL}/deliveries/getreportlocationsummary/?applocationid=${appId}&tenantid=${tenantid}&locationid=${locationid}&fromdate=${startdate}&todate=${enddate}`
);
console.log('getreportlocationsummary', response.data.details);
return response.data.details;
};
// ==============================|| fetchorderdetails (orders detail)||============================== //
export const fetchorderdetails = async ({ queryKey }) => {
console.log('queryKey of fetchorderdetails', queryKey);
const [appId, startdate, enddate, page, rowsPerPage] = queryKey;
const response = await axios.get(
appId == 0
? `${process.env.REACT_APP_URL2}/orders/getorders/?appuserid=${userid}&fromdate=${startdate}&todate=${enddate}&pageno=${page + 1
}&pagesize=${rowsPerPage}`
: `${process.env.REACT_APP_URL2}/orders/getorders/?fromdate=${startdate}&todate=${enddate}&applocationid=${appId}&pageno=${page}&pagesize=${rowsPerPage}`
);
const detailsWithSNo = response.data.details.map((item, index) => ({
...item,
sno: index + 1
}));
console.log('fetchorderdetails', detailsWithSNo);
return detailsWithSNo;
};
// ==============================|| getriderbydelivery (orders detail)||============================== //
export const getriderbydelivery = async (startdate, enddate, appId = 0, tenantid = 0, locationid = 0) => {
// const [, startdate, enddate] = queryKey;
try {
const response = await axios.get(
`${process.env.REACT_APP_URL}/deliveries/getriderbydelivery/?applocationid=${appId}&tenantid=${tenantid}&locationid=${locationid}&fromdate=${startdate}&todate=${enddate}`
);
return response.data.details || [];
} catch (err) {
console.log('getriderbydelivery', err.message);
return [];
}
};
// ==============================|| fetchCount (orders detail)||============================== //
export const fetchCount = async ({ queryKey }) => {
console.log('queryKey of fetchCount', queryKey);
const [appId, startdate, enddate] = queryKey;
let url =
appId == 0
? `${process.env.REACT_APP_URL}/deliveries/deliverysummary/?fromdate=${startdate}&todate=${enddate}`
: `${process.env.REACT_APP_URL}/deliveries/deliverysummary/?applocationid=${appId}&fromdate=${startdate}&todate=${enddate}`;
const response = await axios.get(url);
return response.data.details;
};
// ==============================|| fetchRidersSummary (riders summary)||============================== //
export const fetchRidersSummary = async ({ queryKey }) => {
console.log('queryKey for fetchRidersSummary', queryKey);
const [, appId, startdate, enddate] = queryKey;
const response = await axios.get(
`${process.env.REACT_APP_URL}/deliveries/getridersummary/?applocationid=${appId}&fromdate=${startdate}&todate=${enddate}`
);
console.log('fetchRidersSummary', response.data.details);
return response.data.details;
};
// ==============================|| fetchLocations (orders summary))||============================== //
export const fetchLocations = async () => {
const response = await axios.get(`${process.env.REACT_APP_URL}/partners/getpartners`);
const updatedLocations = [
...response.data.details,
{ partnername: 'All', partnerid: -1 } // Add your new object here
];
console.log('fetchLocations', updatedLocations);
return updatedLocations;
};
// ==============================|| fetchinvoiceinsight (Invoice)||============================== //
export const fetchinvoiceinsight = async () => {
const insightResponse = await axios.get(`${process.env.REACT_APP_URL}/invoice/getinvoiceinsight`);
return insightResponse.data.details;
};
// ==============================|| fetchdeliverylist (Invoice)||============================== //
export const fetchdeliverylist = async ({ queryKey }) => {
const [billStatus] = queryKey;
const deliveyResponse = await axios.get(`${process.env.REACT_APP_URL}/invoice/getallinvoice/?billstatus=${billStatus}`);
console.log('fetchdeliverylist', deliveyResponse.data.details);
return deliveyResponse.data.details;
};
// ==============================|| fetchRidersLogs (RiderLogs)||============================== //
export const fetchRidersLogs = async ({ queryKey }) => {
const [appId, startdate, riderSearch = ''] = queryKey;
const riderLogsResponse = await axios.get(
`${process.env.REACT_APP_URL2}/partners/getriderlogs/?applocationid=${appId}&fromdate=${startdate || ''}&todate=${startdate}&keyword=${riderSearch || ''
} `
);
console.log('fetchRidersLogs', riderLogsResponse.data.details);
return riderLogsResponse.data.details;
};
// ==============================|| getorders (Locations)||============================== //
// fetchOrders.js
export const fetchOrders1 = async ({ pageParam = 1, queryKey }) => {
const [, tenantid, locationid, status, startdate, enddate, searchword, rowsPerPage] = queryKey;
const res = await axios.get(
`${process.env.REACT_APP_URL}/orders/tenant/getorders/?tenantid=${tenantid}&locationid=${locationid}&status=${status}&fromdate=${startdate}&todate=${enddate}&pageno=${pageParam}&pagesize=${rowsPerPage}&keyword=${searchword}`
);
return {
details: res.data.details,
nextPage: res.data.details.length === rowsPerPage ? pageParam + 1 : undefined
};
};
// ==============================|| getusers (viewProfile)||============================== //
export const getusers = async () => {
try {
const res = await axios.get(`${process.env.REACT_APP_URL}/users/getusers/?configid=9&userid=${userid}`);
return res.data.details;
} catch (err) {
console.log('getusers', err.message);
}
};
// ==============================|| getallriders (order)||============================== //
export const getallriders = async () => {
try {
const res = await axios.get(`${process.env.REACT_APP_URL}/partners/getallriders?partnerid=64`);
return res.data.details;
} catch (err) {
console.log('getallriders', err.message);
}
};

View File

@@ -0,0 +1,57 @@
import { Link } from 'react-router-dom';
// material-ui
import { useTheme } from '@mui/material/styles';
import { Box, Button, Grid, Divider, Typography, useMediaQuery } from '@mui/material';
// project import
import useAuth from 'hooks/useAuth';
import AnimateButton from 'components/@extended/AnimateButton';
import AuthWrapper from 'sections/auth/AuthWrapper';
// ================================|| CHECK MAIL ||================================ //
const CheckMail = () => {
const theme = useTheme();
const matchDownSM = useMediaQuery(theme.breakpoints.down('sm'));
const { isLoggedIn } = useAuth();
return (
<AuthWrapper>
<Grid container spacing={3}>
<Grid item xs={12}>
<Box sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
<Typography variant="h3">Hi, Check Your Mail</Typography>
<Typography color="secondary" sx={{ mb: 0.5, mt: 1.25 }}>
We have sent a password recover instructions to your email.
</Typography>
</Box>
</Grid>
<Grid item xs={12}>
<AnimateButton>
<Button
component={Link}
to={isLoggedIn ? '/auth/login' : '/login'}
disableElevation
fullWidth
size="large"
type="submit"
variant="contained"
color="primary"
>
Sign in
</Button>
</AnimateButton>
</Grid>
<Grid item xs={12}>
<Divider>
<Typography variant={matchDownSM ? 'subtitle1' : 'h5'}>Sign up with</Typography>
</Divider>
</Grid>
</Grid>
</AuthWrapper>
);
};
export default CheckMail;

View File

@@ -0,0 +1,29 @@
// material-ui
import { Grid, Stack, Typography } from '@mui/material';
// project import
import AuthWrapper from 'sections/auth/AuthWrapper';
import AuthCodeVerification from 'sections/auth/auth-forms/AuthCodeVerification';
// ================================|| CODE VERIFICATION ||================================ //
const CodeVerification = () => (
<AuthWrapper>
<Grid container spacing={3}>
<Grid item xs={12}>
<Stack spacing={1}>
<Typography variant="h3">Enter Verification Code</Typography>
<Typography color="secondary">We send you on mail.</Typography>
</Stack>
</Grid>
<Grid item xs={12}>
<Typography>We`ve send you code on jone. ****@company.com</Typography>
</Grid>
<Grid item xs={12}>
<AuthCodeVerification />
</Grid>
</Grid>
</AuthWrapper>
);
export default CodeVerification;

View File

@@ -0,0 +1,41 @@
import { Link } from 'react-router-dom';
// material-ui
import { Grid, Stack, Typography } from '@mui/material';
// project import
import useAuth from 'hooks/useAuth';
import AuthWrapper from 'sections/auth/AuthWrapper';
import AuthForgotPassword from 'sections/auth/auth-forms/AuthForgotPassword';
// ================================|| FORGOT PASSWORD ||================================ //
const ForgotPassword = () => {
const { isLoggedIn } = useAuth();
return (
<AuthWrapper>
<Grid container spacing={3}>
<Grid item xs={12}>
<Stack direction="row" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
<Typography variant="h3">Forgot Password</Typography>
<Typography
component={Link}
to={isLoggedIn ? '/auth/login' : '/login'}
variant="body1"
sx={{ textDecoration: 'none' }}
color="primary"
>
Back to Login
</Typography>
</Stack>
</Grid>
<Grid item xs={12}>
<AuthForgotPassword />
</Grid>
</Grid>
</AuthWrapper>
);
};
export default ForgotPassword;

41
src/pages/auth/login.js Normal file
View File

@@ -0,0 +1,41 @@
import { Link } from 'react-router-dom';
// material-ui
import { Grid, Stack, Typography } from '@mui/material';
// project import
import useAuth from 'hooks/useAuth';
import AuthWrapper from 'sections/auth/AuthWrapper';
import AuthLogin from 'sections/auth/auth-forms/AuthLogin';
// ================================|| LOGIN ||================================ //
const Login = () => {
const { isLoggedIn } = useAuth();
return (
<AuthWrapper>
<Grid container spacing={3}>
<Grid item xs={12}>
<Stack direction="row" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
<Typography variant="h3">Login</Typography>
<Typography
component={Link}
to={isLoggedIn ? '/auth/register' : '/register'}
variant="body1"
sx={{ textDecoration: 'none' }}
color="primary"
>
Don&apos;t have an account?
</Typography>
</Stack>
</Grid>
<Grid item xs={12}>
<AuthLogin />
</Grid>
</Grid>
</AuthWrapper>
);
};
export default Login;

View File

@@ -0,0 +1,41 @@
import { Link } from 'react-router-dom';
// material-ui
import { Grid, Stack, Typography } from '@mui/material';
// project import
import useAuth from 'hooks/useAuth';
import AuthWrapper from 'sections/auth/AuthWrapper';
import FirebaseRegister from 'sections/auth/auth-forms/AuthRegister';
// ================================|| REGISTER ||================================ //
const Register = () => {
const { isLoggedIn } = useAuth();
return (
<AuthWrapper>
<Grid container spacing={3}>
<Grid item xs={12}>
<Stack direction="row" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
<Typography variant="h3">Sign up</Typography>
<Typography
component={Link}
to={isLoggedIn ? '/auth/login' : '/login'}
variant="body1"
sx={{ textDecoration: 'none' }}
color="primary"
>
Already have an account?
</Typography>
</Stack>
</Grid>
<Grid item xs={12}>
<FirebaseRegister />
</Grid>
</Grid>
</AuthWrapper>
);
};
export default Register;

View File

@@ -0,0 +1,26 @@
// material-ui
import { Grid, Stack, Typography } from '@mui/material';
// project import
import AuthWrapper from 'sections/auth/AuthWrapper';
import AuthResetPassword from 'sections/auth/auth-forms/AuthResetPassword';
// ================================|| RESET PASSWORD ||================================ //
const ResetPassword = () => (
<AuthWrapper>
<Grid container spacing={3}>
<Grid item xs={12}>
<Stack sx={{ mb: { xs: -0.5, sm: 0.5 } }} spacing={1}>
<Typography variant="h3">Reset Password</Typography>
<Typography color="secondary">Please choose your new password</Typography>
</Stack>
</Grid>
<Grid item xs={12}>
<AuthResetPassword />
</Grid>
</Grid>
</AuthWrapper>
);
export default ResetPassword;

24
src/pages/ctrlK.js Normal file
View File

@@ -0,0 +1,24 @@
import React, { useRef, useEffect } from 'react';
const CtrlK = () => {
useEffect(() => {
const handleKeyPress = (event) => {
if (event.key === 'k' && (event.metaKey || event.ctrlKey)) {
event.preventDefault();
textFieldRef.current.focus();
}
if (event.key === 'Escape' && document.activeElement === textFieldRef.current) {
textFieldRef.current.blur();
}
};
document.addEventListener('keydown', handleKeyPress);
return () => {
document.removeEventListener('keydown', handleKeyPress);
};
}, [textFieldRef]);
};
export default CtrlK;

0
src/pages/demoPage.js Normal file
View File

View File

@@ -0,0 +1,20 @@
// material-ui
import { Typography } from '@mui/material';
// project import
import MainCard from 'components/MainCard';
// ==============================|| SAMPLE PAGE ||============================== //
const SamplePage = () => (
<MainCard title="Sample Card">
<Typography variant="body2">
Lorem ipsum dolor sit amen, consenter nipissing eli, sed do elusion tempos incident ut laborers et doolie magna alissa. Ut enif ad
minim venice, quin nostrum exercitation illampu laborings nisi ut liquid ex ea commons construal. Duos aube grue dolor in reprehended
in voltage veil esse colum doolie eu fujian bulla parian. Exceptive sin ocean cuspidate non president, sunk in culpa qui officiate
descent molls anim id est labours.
</Typography>
</MainCard>
);
export default SamplePage;

View File

@@ -0,0 +1,20 @@
// firebase.js
import firebase from 'firebase/compat/app';
import 'firebase/compat/auth';
import 'firebase/compat/firestore';
const firebaseConfig = {
apiKey: 'AIzaSyBkzz2Yua74Q9YpzGmUPFP94fmJQqNMIiU',
authDomain: 'nearle-gear.firebaseapp.com',
projectId: 'nearle-gear',
storageBucket: 'nearle-gear.appspot.com',
messagingSenderId: '140444764229',
appId: '1:140444764229:web:2e60cbb7b4e26b33283b2c'
};
// Initialize Firebase
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
}
export default firebase; // Export the initialized firebase object

View File

@@ -0,0 +1,56 @@
import { Link } from 'react-router-dom';
// project import
import { APP_DEFAULT_PATH } from 'config';
// material-ui
import { Box, Button, Grid, Stack, Typography } from '@mui/material';
// assets
import error404 from 'assets/images/maintenance/Error404.png';
import TwoCone from 'assets/images/maintenance/TwoCone.png';
// ==============================|| ERROR 404 - MAIN ||============================== //
function Error404() {
return (
<>
<Grid
container
spacing={10}
direction="column"
alignItems="center"
justifyContent="center"
sx={{ minHeight: '100vh', pt: 1.5, pb: 1, overflow: 'hidden' }}
>
<Grid item xs={12}>
<Stack direction="row">
<Grid item>
<Box sx={{ width: { xs: 250, sm: 590 }, height: { xs: 130, sm: 300 } }}>
<img src={error404} alt="mantis" style={{ width: '100%', height: '100%' }} />
</Box>
</Grid>
<Grid item sx={{ position: 'relative' }}>
<Box sx={{ position: 'absolute', top: 60, left: -40, width: { xs: 130, sm: 390 }, height: { xs: 115, sm: 330 } }}>
<img src={TwoCone} alt="mantis" style={{ width: '100%', height: '100%' }} />
</Box>
</Grid>
</Stack>
</Grid>
<Grid item xs={12}>
<Stack spacing={2} justifyContent="center" alignItems="center">
<Typography variant="h1">Page Not Found</Typography>
<Typography color="textSecondary" align="center" sx={{ width: { xs: '73%', sm: '61%' } }}>
The page you are looking was moved, removed, renamed, or might never exist!
</Typography>
<Button component={Link} to={APP_DEFAULT_PATH} variant="contained">
Back To Home
</Button>
</Stack>
</Grid>
</Grid>
</>
);
}
export default Error404;

View File

@@ -0,0 +1,45 @@
import { Link } from 'react-router-dom';
// project import
import { APP_DEFAULT_PATH } from 'config';
// material-ui
import { useTheme } from '@mui/material/styles';
import { useMediaQuery, Box, Button, Grid, Stack, Typography } from '@mui/material';
// assets
import error500 from 'assets/images/maintenance/Error500.png';
// ==============================|| ERROR 500 - MAIN ||============================== //
function Error500() {
const theme = useTheme();
const matchDownSM = useMediaQuery(theme.breakpoints.down('sm'));
return (
<>
<Grid container direction="column" alignItems="center" justifyContent="center" sx={{ minHeight: '100vh' }}>
<Grid item xs={12}>
<Box sx={{ width: { xs: 350, sm: 396 } }}>
<img src={error500} alt="mantis" style={{ height: '100%', width: '100%' }} />
</Box>
</Grid>
<Grid item xs={12}>
<Stack justifyContent="center" alignItems="center">
<Typography align="center" variant={matchDownSM ? 'h2' : 'h1'}>
Internal Server Error
</Typography>
<Typography color="textSecondary" variant="body2" align="center" sx={{ width: { xs: '73%', sm: '70%' }, mt: 1 }}>
Server error 500. we fixing the problem. please try again at a later stage.
</Typography>
<Button component={Link} to={APP_DEFAULT_PATH} variant="contained" sx={{ textTransform: 'none', mt: 4 }}>
Back To Home
</Button>
</Stack>
</Grid>
</Grid>
</>
);
}
export default Error500;

View File

@@ -0,0 +1,94 @@
import PropTypes from 'prop-types';
// material-ui
import { useTheme } from '@mui/material/styles';
import { useMediaQuery, Box, Button, Grid, Stack, TextField, Typography } from '@mui/material';
// third party
import { useTimer } from 'react-timer-hook';
// assets
import coming from 'assets/images/maintenance/coming-soon.png';
import MainCard from 'components/MainCard';
// ==============================|| COMING SOON - MAIN ||============================== //
const TimerBox = ({ count, label }) => {
const theme = useTheme();
const matchDownSM = useMediaQuery(theme.breakpoints.down('sm'));
return (
<MainCard content={false} sx={{ width: { xs: 60, sm: 80 } }}>
<Stack justifyContent="center" alignItems="center">
<Box sx={{ py: 1.75 }}>
<Typography variant={matchDownSM ? 'h4' : 'h2'}>{count}</Typography>
</Box>
<Box sx={{ p: 0.5, bgcolor: 'secondary.lighter', width: '100%' }}>
<Typography align="center" variant="subtitle2">
{label}
</Typography>
</Box>
</Stack>
</MainCard>
);
};
TimerBox.propTypes = {
count: PropTypes.number,
label: PropTypes.string
};
function ComingSoon() {
const time = new Date();
time.setSeconds(time.getSeconds() + 3600 * 24 * 2 - 3600 * 15.5);
const { seconds, minutes, hours, days } = useTimer({ expiryTimestamp: time });
return (
<>
<Grid container spacing={4} direction="column" alignItems="center" justifyContent="center" sx={{ minHeight: '100vh', py: 2 }}>
<Grid item xs={12}>
<Box sx={{ height: { xs: 310, sm: 420 }, width: { xs: 360, sm: 'auto' } }}>
<img src={coming} alt="mantis" style={{ height: '100%', width: '100%' }} />
</Box>
</Grid>
<Grid item xs={12}>
<Stack spacing={1} justifyContent="center" alignItems="center" sx={{ mt: -2 }}>
<Typography align="center" variant="h1">
Coming Soon
</Typography>
<Typography align="center" color="textSecondary">
Something new is on its way
</Typography>
</Stack>
</Grid>
<Grid item xs={12} sx={{ width: { xs: '95%', md: '40%' } }}>
<Stack direction="row" alignItems="center" justifyContent="center" spacing={{ xs: 1, sm: 2 }}>
<TimerBox count={days} label="day" />
<Typography variant="h1"> : </Typography>
<TimerBox count={hours} label="hour" />
<Typography variant="h1"> : </Typography>
<TimerBox count={minutes} label="min" />
<Typography variant="h1"> : </Typography>
<TimerBox count={seconds} label="sec" />
</Stack>
</Grid>
<Grid item xs={12} sx={{ width: { xs: 380, md: '40%', lg: '30%' } }}>
<Stack spacing={2} sx={{ mt: 2 }}>
<Typography align="center" color="textSecondary">
Be the first to be notified when Mantis launches.
</Typography>
<Stack direction="row" spacing={1}>
<TextField fullWidth placeholder="Email Address" />
<Button variant="contained" sx={{ width: '50%' }}>
Notify Me
</Button>
</Stack>
</Stack>
</Grid>
</Grid>
</>
);
}
export default ComingSoon;

View File

@@ -0,0 +1,39 @@
import { Link } from 'react-router-dom';
// project import
import { APP_DEFAULT_PATH } from 'config';
// material-ui
import { Box, Button, Grid, Stack, Typography } from '@mui/material';
// assets
import construction from 'assets/images/maintenance/under-construction.svg';
// ==============================|| UNDER CONSTRUCTION - MAIN ||============================== //
function UnderConstruction() {
return (
<Grid container spacing={4} direction="column" alignItems="center" justifyContent="center" sx={{ minHeight: '100vh', py: 2 }}>
<Grid item xs={12}>
<Box sx={{ width: { xs: 300, sm: 480 } }}>
<img src={construction} alt="mantis" style={{ width: '100%', height: 'auto' }} />
</Box>
</Grid>
<Grid item xs={12}>
<Stack spacing={2} justifyContent="center" alignItems="center">
<Typography align="center" variant="h1">
Under Construction
</Typography>
<Typography color="textSecondary" align="center" sx={{ width: '85%' }}>
Hey! Please check out this site later. We are doing some maintenance on it right now.
</Typography>
<Button component={Link} to={APP_DEFAULT_PATH} variant="contained">
Back To Home
</Button>
</Stack>
</Grid>
</Grid>
);
}
export default UnderConstruction;

View File

@@ -0,0 +1,43 @@
import { Grid } from '@mui/material';
import { useQuery } from '@tanstack/react-query';
import CircularLoader from 'components/CircularLoader';
import Loader from 'components/Loader';
import MainCard from 'components/MainCard';
import { getusers } from 'pages/api/api';
const ViewProfile = () => {
const {
data: userdata,
isLoading,
isError,
error
} = useQuery({
queryKey: ['getuser'],
queryFn: getusers
});
return (
<>
{isLoading && (
<>
<Loader />
<CircularLoader />
</>
)}
<MainCard>
<Grid container spacing={3}>
<Grid item xs={12} sm={6} md={4}>
{userdata?.firstname}
</Grid>
<Grid item xs={12} sm={6} md={4}>
{' '}
</Grid>
<Grid item xs={12} sm={6} md={4}>
{' '}
</Grid>
</Grid>
</MainCard>
</>
);
};
export default ViewProfile;

View File

@@ -0,0 +1,132 @@
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import { Stack, TableContainer, Table, TableHead, TableRow, TableCell, TableBody, Chip } from '@mui/material';
import MainCard from 'components/MainCard';
import Loader from 'components/Loader';
import TitleCard from 'components/nearle_components/TitleCard';
import LocationAutocomplete from 'components/nearle_components/LocationAutocomplete';
import { Empty } from 'antd';
import { OrdersTableSkeleton } from '../orders/OrdersTableSkeleton';
// ==============================|| Starts here ||============================== //
const ClientsPricing = () => {
const [appId, setAppId] = useState(0);
const [locaName, setLocoName] = useState('');
const [pricing, setpricing] = useState([]);
const [isLoader, setIsloader] = useState([]);
// ==============================|| formatNumberToRupees ||============================== //
function formatNumberToRupees(value) {
return new Intl.NumberFormat('en-IN', {
style: 'currency',
currency: 'INR',
minimumFractionDigits: 2
}).format(value);
}
function dotzerozero(value) {
return new Intl.NumberFormat('en-IN', {
minimumFractionDigits: 2
}).format(value);
}
// ==============================|| getAllPricing ||============================== //
const getAllPricing = async () => {
setIsloader(true);
try {
const pricingres = await axios.get(`${process.env.REACT_APP_URL}/utils/getallpricing/?applocationid=${appId}`);
console.log('pricingres', pricingres.data.details);
setpricing(pricingres.data.details);
setIsloader(false);
} catch (err) {
console.log('pricingres', err);
}
};
useEffect(() => {
getAllPricing();
}, [appId]);
return (
<>
{isLoader && <Loader />}
{/* ============================================= || Title Card || ============================================= */}
<Stack direction={'row'} justifyContent="space-between" alignItems="center" sx={{ my: 1 }}>
<TitleCard title="Pricing" />
<Stack sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center', justifyContent: 'center' }}>
<LocationAutocomplete
locaName={locaName}
setAppId={setAppId}
setLocoName={setLocoName}
sx={{ width: { xs: '100%', custom450: 300 }, zIndex: '100' }}
/>
</Stack>
</Stack>
{/* ============================================= || table || ============================================= */}
<MainCard content={false} sx={{ mt: 2 }}>
<TableContainer>
<Table>
<TableHead sx={{ bgcolor: '#e1bee7' }}>
<TableRow>
<TableCell>S.No</TableCell>
<TableCell>Location</TableCell>
<TableCell align="center">Pricing Id</TableCell>
<TableCell>Name</TableCell>
<TableCell>Slab</TableCell>
<TableCell align="center">Base Price</TableCell>
<TableCell align="center">MinKm</TableCell>
<TableCell align="center">Price/Km</TableCell>
<TableCell align="center">MaxKm</TableCell>
<TableCell>Min Orders</TableCell>
</TableRow>
</TableHead>
<TableBody>
{isLoader && <OrdersTableSkeleton col={5} />}
{pricing?.length === 0 && !isLoader ? (
<TableRow>
<TableCell colSpan={10} align="center">
<Empty description={'No Pricing List'} />
</TableCell>
</TableRow>
) : (
pricing?.map((data, index) => (
<TableRow key={data.pricingid || index}>
<TableCell>{index + 1}</TableCell>
<TableCell>{data.applocation}</TableCell>
<TableCell align="center">
<Chip size="small" color="info" label={data.pricingid} sx={{ width: 50 }} />
</TableCell>
<TableCell sx={{ whiteSpace: 'nowrap' }}>{data.appname}</TableCell>
<TableCell>{data.slab}</TableCell>
<TableCell align="center">
<Chip size="small" color="primary" label={formatNumberToRupees(data.baseprice)} sx={{ width: 100 }} />
</TableCell>
<TableCell align="center">
<Chip size="small" color="warning" label={dotzerozero(data.minkm)} sx={{ width: 100 }} />
</TableCell>
<TableCell align="center">
<Chip size="small" color="success" label={formatNumberToRupees(data.priceperkm)} sx={{ width: 100 }} />
</TableCell>
<TableCell align="center">
<Chip size="small" color="error" label={dotzerozero(data.maxkm)} sx={{ width: 100 }} />
</TableCell>
<TableCell>{data.minorder}</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
</TableContainer>
</MainCard>
</>
);
};
export default ClientsPricing;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,537 @@
import { React, useEffect, useState, useRef } from 'react';
import { useTheme } from '@mui/material/styles';
import { Button, Grid, InputLabel, MenuItem, Select, Stack, TextField, Typography, IconButton, Autocomplete } from '@mui/material';
import MainCard from 'components/MainCard';
import axios from 'axios';
import Loader from 'components/Loader';
import Geocode from 'react-geocode';
import { enqueueSnackbar } from 'notistack';
import { useNavigate } from 'react-router';
import CloseIcon from '@mui/icons-material/Close';
import LocationAutocomplete from 'components/nearle_components/LocationAutocomplete';
import { OpenToast } from 'components/third-party/OpenToast';
const CreateCustomer = () => {
const [appId, setAppId] = useState(0);
const locationRef = useRef(null);
const [mobilenumber, setMobilenumber] = useState('');
const [emailaddress, setEmailaddress] = useState('');
const [address, setAddress] = useState('');
const [firstname, setFirstname] = useState('');
const [doorno, setDoorno] = useState('');
const [landmark, setLandmark] = useState('');
const [inputValue2, setInputValue2] = useState('');
const [appLocaLat, setAppLocaLat] = useState();
const [appLocaLng, setAppLocaLng] = useState();
const [appLocaRadius, setAppLocaRadius] = useState();
const [locaName, setLocoName] = useState('Select Location');
const [tenantlist, setTenantlist] = useState([]);
const [tid, setTid] = useState(0);
const [pickCust, setPickCust] = useState({});
const [startPoint, setStartPoint] = useState({ latitude: 0, longitude: 0 });
const [loading, setLoading] = useState(false);
const navigate = useNavigate();
Geocode.setApiKey(process.env.REACT_APP_GOOGLE_MAPS_API_KEY);
useEffect(() => {
// Initialize Google Maps Autocomplete
if (inputValue2) {
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();
setInputValue2(`${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() });
setAddress(`${place.name} ${place.formatted_address}`);
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.administrative_area_level_3,
city: address.locality,
state: address.administrative_area_level_1,
postcode: address.postal_code,
latitude: place.geometry.location.lat(),
longitude: place.geometry.location.lng()
});
console.log('Pick Address:', address);
});
}
}, [inputValue2]);
// ==================================================== || getapplocations || ====================================================
const getapplocations = async () => {
setLoading(true);
await axios
.get(`${process.env.REACT_APP_URL}/utils/getapplocations/?applocationid=${appId}`)
.then((res) => {
console.log('getapplocations', res);
const { latitude, longitude, radius } = res.data.details[0];
if (res.data.status) {
setAppLocaLat(latitude);
setAppLocaLng(longitude);
setAppLocaRadius(radius);
console.log('radius', radius);
}
setLoading(false);
})
.catch((err) => {
console.log(err);
setLoading(false);
});
};
useEffect(() => {
if (appId) {
getapplocations();
}
}, [appId]);
// ===================================================== || fetchtenantinfolist || =====================================================
const fetchtenantinfolist = async (id) => {
setLoading(true);
await axios
.get(`${process.env.REACT_APP_URL}/tenants/gettenants/?applocationid=${id}&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);
}, [appId]);
// ============================================= || 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) {
} else {
}
} catch (err) {
console.log('gettenantlocations', err);
}
};
const opentoast = (message) => {
enqueueSnackbar(message, {
variant: 'error',
anchorOrigin: { vertical: 'top', horizontal: 'right' },
autoHideDuration: 2000
});
};
const createprofile = async () => {
let obj = {
applocationid: +appId,
tenantid: +tid,
customerid: 0,
configid: 1,
firstname: firstname,
dialcode: '+91',
contactno: mobilenumber,
email: emailaddress,
doorno: doorno,
address: pickCust.address,
suburb: pickCust.suburb,
city: pickCust.city,
state: pickCust.state,
postcode: pickCust.postcode,
landmark: landmark,
latitude: startPoint.latitude.toString(),
longitude: startPoint.longitude.toString(),
profileimage: '',
devicetype: '',
deviceid: '',
customertoken: '',
primaryaddress: 1
};
console.log(obj);
setLoading(true);
try {
await axios
.post(`${process.env.REACT_APP_URL}/customers/create`, obj)
.then((res) => {
console.log(res);
if (res.data.status) {
enqueueSnackbar(' Created Successfully ', {
variant: 'success',
anchorOrigin: { vertical: 'top', horizontal: 'right' },
autoHideDuration: 2000
});
navigate('/nearle/customers');
} else if (res.data.message == 'Customer Already available') {
enqueueSnackbar('Customer Already available', {
variant: 'error',
anchorOrigin: { vertical: 'top', horizontal: 'right' },
autoHideDuration: 2000
});
}
setLoading(false);
})
.catch((err) => {
console.log(err);
setLoading(false);
enqueueSnackbar(err.message, {
variant: 'error',
anchorOrigin: { vertical: 'top', horizontal: 'right' },
autoHideDuration: 2000
});
});
} catch (err) {
console.log(err);
setLoading(false);
}
};
return (
<>
{loading && <Loader />}
<Grid item xs={12} sx={{ mb: 2 }}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Typography variant="h3">Create Customer</Typography>
</Stack>
</Grid>
<MainCard>
<Grid container spacing={3}>
<Grid item xs={12}>
<Grid container spacing={3}>
{/* ===================================================== || Choose location || ===================================================== */}
<Grid item xs={12} md={6}>
<LocationAutocomplete ref={locationRef} locaName={locaName} setAppId={setAppId} setLocoName={setLocoName} sx={{}} />
</Grid>
{/* ===================================================== || Choose client || ===================================================== */}
<Grid item xs={12} md={6}>
<Autocomplete
fullWidth
disabled={appId == 0}
id="free-solo-demo"
sx={{}}
options={tenantlist || []}
renderInput={(params) => <TextField {...params} label="Choose Client" focused />}
onChange={(e, val, reason) => {
if (val) {
console.log('Client', val);
gettenantlocations(val.tenantid);
setTid(val.tenantid);
} else {
setClientinfo({});
setTenantid('');
}
if (reason == 'clear') {
}
}}
/>{' '}
</Grid>
{/* ===================================================== || Name|| ===================================================== */}
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-last-name">Name</InputLabel>
<TextField
fullWidth
id="personal-last-name"
placeholder="Name"
onChange={(e) => setFirstname(e.target.value)}
value={firstname}
autoComplete="off"
/>
</Stack>
</Grid>
{/* ===================================================== || Phone Number || ===================================================== */}
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-phone">Phone Number</InputLabel>
<Stack direction="row" justifyContent="space-between" alignItems="center" spacing={2}>
<Select defaultValue="+1" disabled sx={{ cursor: 'not-allowed' }}>
<MenuItem value="+1">+91</MenuItem>
</Select>
<TextField
type="number"
id="personal-phone"
fullWidth
placeholder="Phone Number"
onChange={(e) => {
if (e.target.value.toString().length <= 10) {
setMobilenumber(e.target.value);
}
}}
value={mobilenumber}
autoComplete="off"
// disabled
sx={{ cursor: 'not-allowed' }}
/>
</Stack>
</Stack>
</Grid>
{/* ===================================================== || Email|| ===================================================== */}
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-email">Email </InputLabel>
<TextField
type="email"
fullWidth
id="personal-email"
placeholder="Email "
onChange={(e) => setEmailaddress(e.target.value)}
value={emailaddress}
autoComplete="off"
/>
</Stack>
</Grid>
{/* ===================================================== || door no || ===================================================== */}
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-location">Door No</InputLabel>
<TextField
fullWidth
// defaultValue="New York"
id="personal-location"
placeholder="Door No"
onChange={(e) => setDoorno(e.target.value)}
value={doorno}
autoComplete="off"
/>
</Stack>
</Grid>
{/* ===================================================== || Address || ===================================================== */}
<Grid item xs={12}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-email"> Address</InputLabel>
<TextField
variant="outlined"
id="addressAuto1"
fullWidth
value={inputValue2}
onChange={(e) => {
if (appId) {
appId && setInputValue2(e.target.value);
} else {
OpenToast('Select Location First', 'warning', 3000);
}
}}
InputProps={{
endAdornment: (
<IconButton
onClick={() => {
setInputValue2('');
setPickCust({
...pickCust,
doorno: '',
suburb: '',
city: '',
postcode: '',
landmark: ''
});
setStartPoint({ latitude: 0, longitude: 0 });
}}
size="small"
>
<CloseIcon />
</IconButton>
)
}}
/>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-location">Location</InputLabel>
<TextField
fullWidth
// defaultValue="New York"
id="personal-location"
placeholder="Location"
onChange={(e) => setPickCust({ ...pickCust, suburb: e.target.value })}
value={pickCust.suburb}
autoComplete="off"
/>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-zipcode">City</InputLabel>
<TextField
fullWidth
id="personal-zipcode"
placeholder="City"
onChange={(e) => setPickCust({ ...pickCust, city: e.target.value })}
value={pickCust.city}
autoComplete="off"
/>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-location">State</InputLabel>
<TextField
fullWidth
// defaultValue="New York"
id="personal-location"
placeholder="State"
onChange={(e) => setPickCust({ ...pickCust, state: e.target.value })}
value={pickCust.state}
autoComplete="off"
/>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-zipcode">Post Code</InputLabel>
<TextField
fullWidth
// defaultValue="956754"
type="number"
id="personal-zipcode"
placeholder="Zipcode"
onChange={(e) => setPickCust({ ...pickCust, postcode: e.target.value })}
value={pickCust.postcode}
autoComplete="off"
/>
</Stack>
</Grid>
<Grid item xs={12}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-email">Landmark</InputLabel>
<TextField
type="email"
fullWidth
// defaultValue="stebin.ben@gmail.com"
id="personal-email"
placeholder="Landmark"
onChange={(e) => setLandmark(e.target.value)}
value={landmark}
autoComplete="off"
/>
</Stack>
</Grid>
</Grid>
</Grid>
<Grid item xs={12}>
<Stack direction="row" justifyContent="flex-end" alignItems="center" spacing={2}>
<Button
variant="contained"
onClick={() => {
if (appId === '') {
opentoast('Select Applocation ');
} else if (tid === '') {
opentoast('Select Tenant');
} else if (firstname === '') {
opentoast('Enter Name');
} else if (mobilenumber === '') {
opentoast('Enter Mobile Number ');
} else if (address === '') {
opentoast('Enter Address ');
} else if (pickCust.city === '') {
opentoast('Enter City ');
} else if (pickCust.state === '') {
opentoast('Enter State ');
} else if (pickCust.suburb === '') {
opentoast('Enter location ');
} else if (pickCust.postcode === '') {
opentoast('Enter Post Code ');
} else if (landmark === '') {
opentoast('Enter Land Mark ');
} else if (pickCust.latitude === '') {
opentoast('Invalid latitude ');
} else if (pickCust.longitude === '') {
opentoast('Invaiid Longitude ');
} else {
createprofile();
}
}}
>
Create
</Button>
</Stack>
</Grid>
</Grid>
</MainCard>
</>
);
};
export default CreateCustomer;

View File

@@ -0,0 +1,604 @@
import { useEffect, useState } from 'react';
// material-ui
import { useTheme } from '@mui/material/styles';
import { Box, Button, FormLabel, Grid, InputLabel, MenuItem, Select, Stack, TextField, Typography } from '@mui/material';
// third-party
// import { PatternFormat } from 'react-number-format';
// project import
import MainCard from 'components/MainCard';
import axios from 'axios';
import { usePlacesWidget } from 'react-google-autocomplete';
import Loader from 'components/Loader';
import Geocode from 'react-geocode';
import { enqueueSnackbar } from 'notistack';
import { useNavigate } from 'react-router';
// import { setLocationType } from 'react-geocode';
// const avatarImage = require.context('assets/images/users', true);
// styles & constant
// const ITEM_HEIGHT = 48;
// const ITEM_PADDING_TOP = 8;
// const MenuProps = {
// PaperProps: {
// style: {
// maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP
// }
// }
// };
const Createclient = () => {
const theme = useTheme();
// const [role, setRole] = useState('');
const [mobilenumber, setMobilenumber] = useState('');
const [emailaddress, setEmailaddress] = useState('');
const [city, setCity] = useState('');
const [zipcode, setZipcode] = useState('');
const [address, setAddress] = useState('');
const [state, setState] = useState('');
const [suburb, setSuburb] = useState('');
const [latlong, setLatlong] = useState({});
const [firstname, setFirstname] = useState('');
const [doorno, setDoorno] = useState('');
const [landmark, setLandmark] = useState('');
const [tenantinfo, setTenantinfo] = useState({});
const navigate = useNavigate();
Geocode.setApiKey(process.env.REACT_APP_GOOGLE_MAPS_API_KEY);
// Geocode.setApiKey('AIzaSyCF4KatYCI3vqz1_H3kiHeyS3yCMfYToh8');
const [loading, setLoading] = useState(false);
useEffect(() => {
// fetchprofiledetails(localStorage.getItem('appuserid'));
// fetchprofiledetails(181);
if (localStorage.getItem('tenantid')) {
fetchtenantinfo(localStorage.getItem('tenantid'));
}
}, []);
useEffect(() => {
try {
Geocode.fromAddress(address).then(
(response) => {
if (response.status == 'OK') {
const { lat, lng } = response.results[0].geometry.location;
setLatlong({
lat,
lng
});
console.log(response);
}
},
(error) => {
console.log(error);
}
);
} catch (err) {
console.log(err);
}
}, [address]);
const opentoast = (message) => {
enqueueSnackbar(message, {
variant: 'error',
anchorOrigin: { vertical: 'top', horizontal: 'right' },
autoHideDuration: 2000
});
// console.log(alertmessage)
};
const fetchprofiledetails = async (userid) => {
if (userid) {
setLoading(true);
try {
await axios
.get(`${process.env.REACT_APP_URL2}/tenants/getclient?id=${userid}`)
.then((res) => {
console.log(res);
if (res.data.message === 'Successful') {
let res1 = res.data.details;
setMobilenumber(res1.contactno);
setEmailaddress(res1.primaryemail);
setAddress(res1.address);
setCity(res1.city);
setZipcode(res1.postcode);
setState(res1.state);
setSuburb(res1.suburb);
setLatlong({
lat: res1.latitude,
lng: res1.longitude
});
}
setLoading(false);
})
.catch((err) => {
console.log(err);
setLoading(false);
});
} catch (err) {
console.log(err);
setLoading(false);
}
}
};
const fetchtenantinfo = async (tid) => {
setLoading(true);
await axios
.get(`${process.env.REACT_APP_URL}/tenants/gettenantinfo/?tenantid=${tid}`)
.then((res) => {
console.log(res);
if (res.data.status) {
setTenantinfo(res.data.details);
}
setLoading(false);
})
.catch((err) => {
console.log(err);
setLoading(false);
});
};
useEffect(() => {
if (selectedImage) {
setAvatar(URL.createObjectURL(selectedImage));
}
}, [selectedImage]);
const { ref: materialRef } = usePlacesWidget({
apiKey: process.env.REACT_APP_GOOGLE_MAPS_API_KEY,
onPlaceSelected: (place) => {
console.log(place);
setAddress(place.formatted_address);
let city1, zipcode1, state1, suburb1;
for (let i = 0; i < place.address_components.length; i++) {
for (let j = 0; j < place.address_components[i].types.length; j++) {
switch (place.address_components[i].types[j]) {
case 'locality':
city1 = place.address_components[i].long_name;
break;
case 'administrative_area_level_1':
state1 = place.address_components[i].long_name;
break;
case 'postal_code':
zipcode1 = place.address_components[i].long_name;
break;
case 'sublocality':
suburb1 = place.address_components[i].long_name;
break;
}
}
}
setCity(city1 || '');
setState(state1 || '');
setZipcode(zipcode1 || '');
setSuburb(suburb1 || '');
// setAddress(place.formatted_address)
},
// inputAutocompleteValue: "country",
options: {
// componentRestrictions: 'us',
// types: ["establishment"]
types: ['address' || 'geocode']
}
});
const createprofile = async () => {
console.log('res', businessname, businessno, mobilenumber, emailaddress, address, city, zipcode);
// if (!businessname) {
// opentoast('Fill Business name')
// } else if (!businessno) {
// opentoast('Fill Registration No')
// }
// else
if (!firstname) {
opentoast('Fill Full name');
} else if (!mobilenumber) {
opentoast('Fill Mobile Number');
} else if (!emailaddress) {
opentoast('Fill emailaddress');
} else if (!address) {
opentoast('Fill Address');
} else if (!city) {
opentoast('Fill City');
} else if (!zipcode) {
opentoast('Fill post code');
} else if (!suburb) {
opentoast('Fill suburb');
} else if (!latlong.lat || !latlong.lng) {
opentoast('Choose valid address');
} else {
let obj = {
customerid: 0,
configid: 1,
firstname: firstname,
applocationid: tenantinfo.applolcationid,
profileimage: '',
dialcode: '+91',
contactno: mobilenumber,
devicetype: '',
deviceid: '',
customertoken: '',
address: address,
suburb: suburb,
city: city,
state: state,
postcode: zipcode,
landmark: landmark,
doorno: doorno,
latitude: latlong.lat.toString(),
longitude: latlong.lng.toString(),
tenantid: parseInt(localStorage.getItem('tenantid')),
email: emailaddress
};
console.log(obj);
setLoading(true);
try {
await axios
.post(`${process.env.REACT_APP_URL}/customers/create`, obj)
.then((res) => {
console.log(res);
if (res.data.status) {
enqueueSnackbar(' Created Successfully ', {
variant: 'success',
anchorOrigin: { vertical: 'top', horizontal: 'right' },
autoHideDuration: 2000
});
navigate('/clients');
// setTimeout(()=>{
// fetchprofiledetails(localStorage.getItem('appuserid'));
// },2000)
} else if (res.data.message == 'Customer Already available') {
enqueueSnackbar('Customer Already available', {
variant: 'error',
anchorOrigin: { vertical: 'top', horizontal: 'right' },
autoHideDuration: 2000
});
}
setLoading(false);
})
.catch((err) => {
console.log(err);
setLoading(false);
enqueueSnackbar(err.message, {
variant: 'error',
anchorOrigin: { vertical: 'top', horizontal: 'right' },
autoHideDuration: 2000
});
});
} catch (err) {
console.log(err);
setLoading(false);
}
}
};
// const [experience, setExperience] = useState('0');
// const handleChange = (event) => {
// setExperience(event.target.value);
// };
return (
<>
{loading && <Loader />}
<Grid item xs={12} sx={{ mb: 2 }}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Typography variant="h3">Create Client</Typography>
</Stack>
</Grid>
<MainCard>
<Grid container spacing={3}>
{/* <Grid item xs={12} sm={4} >
<MainCard title="Personal Information" sx={{ height: '100%' }}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Stack spacing={2.5} alignItems="center" sx={{ m: 3 }}>
<FormLabel
htmlFor="change-avtar"
sx={{
position: 'relative',
borderRadius: '50%',
overflow: 'hidden',
'&:hover .MuiBox-root': { opacity: 1 },
cursor: 'pointer'
}}
>
<Avatar alt="Avatar 1"
src={avatar}
sx={{ width: 76, height: 76 }} />
<Box
sx={{
position: 'absolute',
top: 0,
left: 0,
backgroundColor: theme.palette.mode === ThemeMode.DARK ? 'rgba(255, 255, 255, .75)' : 'rgba(0,0,0,.65)',
width: '100%',
height: '100%',
opacity: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
<Stack spacing={0.5} alignItems="center">
<CameraOutlined style={{ color: theme.palette.secondary.lighter, fontSize: '1.5rem' }} />
<Typography sx={{ color: 'secondary.lighter' }} variant="caption">
Upload
</Typography>
</Stack>
</Box>
</FormLabel>
<TextField
type="file"
accept="image/*"
id="change-avtar"
placeholder="Outlined"
variant="outlined"
sx={{ display: 'none' }}
onChange={(e) => setSelectedImage(e.target.files?.[0])}
/>
</Stack>
</Grid>
<Grid item xs={12}
>
</Grid>
<Grid item xs={12}
>
</Grid>
<Grid item xs={12}
>
</Grid>
<Grid item xs={12}
>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-role-name">Role</InputLabel>
<TextField fullWidth
id="personal-role-name" placeholder="Role Name" autoFocus
onChange={(e) => setRole(e.target.value)}
value={role}
autoComplete='off'
/>
</Stack>
</Grid>
</Grid>
</MainCard>
</Grid> */}
<Grid
item
xs={12}
// sm={8}
>
<MainCard
// title="Contact Information"
sx={{ height: '100%' }}
>
<Grid container spacing={3}>
{/* <Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-first-name">Business Name</InputLabel>
<TextField fullWidth
id="personal-first-name" placeholder="Business Name" autoFocus
onChange={(e) => setBusinessname(e.target.value)}
value={businessname}
autoComplete='off'
/>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-last-name">Registration No</InputLabel>
<TextField fullWidth
id="personal-last-name" placeholder="Registration No"
onChange={(e) => setBusinessno(e.target.value)}
value={businessno}
autoComplete='off'
/>
</Stack>
</Grid> */}
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-last-name">Admin Name</InputLabel>
<TextField
fullWidth
id="personal-last-name"
placeholder="Name"
onChange={(e) => setFirstname(e.target.value)}
value={firstname}
autoComplete="off"
/>
</Stack>
</Grid>
<Grid item xs={12} sm={6}></Grid>
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-phone">Phone Number</InputLabel>
<Stack direction="row" justifyContent="space-between" alignItems="center" spacing={2}>
<Select defaultValue="+1" disabled sx={{ cursor: 'not-allowed' }}>
<MenuItem value="+1">+91</MenuItem>
</Select>
<TextField
type="number"
id="personal-phone"
// format="##########"
// mask="_"
fullWidth
// customInput={TextField}
placeholder="Phone Number"
// defaultValue="8654239581"
// onBlur={() => { }}
onChange={(e) => {
if (e.target.value.toString().length <= 10) {
setMobilenumber(e.target.value);
}
}}
value={mobilenumber}
autoComplete="off"
// disabled
sx={{ cursor: 'not-allowed' }}
/>
</Stack>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-email">Email Address</InputLabel>
<TextField
type="email"
fullWidth
// defaultValue="stebin.ben@gmail.com"
id="personal-email"
placeholder="Email Address"
onChange={(e) => setEmailaddress(e.target.value)}
value={emailaddress}
autoComplete="off"
/>
</Stack>
</Grid>
<Grid item xs={12}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-address">Address</InputLabel>
<TextField
fullWidth
// defaultValue="Street 110-B Kalians Bag, Dewan, M.P. New York"
id="personal-address"
placeholder="Address"
value={address}
onChange={(e) => setAddress(e.target.value)}
inputRef={materialRef}
/>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-location">Suburb</InputLabel>
<TextField
fullWidth
// defaultValue="New York"
id="personal-location"
placeholder="Location"
onChange={(e) => setSuburb(e.target.value)}
value={suburb}
autoComplete="off"
/>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-zipcode">City</InputLabel>
<TextField
fullWidth
// defaultValue="956754"
// type='number'
id="personal-zipcode"
placeholder="City"
onChange={(e) => setCity(e.target.value)}
value={city}
autoComplete="off"
/>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-location">State</InputLabel>
<TextField
fullWidth
// defaultValue="New York"
id="personal-location"
placeholder="State"
onChange={(e) => setState(e.target.value)}
value={state}
autoComplete="off"
/>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-zipcode">Post Code</InputLabel>
<TextField
fullWidth
// defaultValue="956754"
type="number"
id="personal-zipcode"
placeholder="Zipcode"
onChange={(e) => setZipcode(e.target.value)}
value={zipcode}
autoComplete="off"
/>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-location">Door No</InputLabel>
<TextField
fullWidth
// defaultValue="New York"
id="personal-location"
placeholder="Door No"
onChange={(e) => setDoorno(e.target.value)}
value={doorno}
autoComplete="off"
/>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-email">Landmark</InputLabel>
<TextField
type="email"
fullWidth
// defaultValue="stebin.ben@gmail.com"
id="personal-email"
placeholder="Landmark"
onChange={(e) => setLandmark(e.target.value)}
value={landmark}
autoComplete="off"
/>
</Stack>
</Grid>
</Grid>
</MainCard>
</Grid>
<Grid item xs={12}>
<Stack direction="row" justifyContent="flex-end" alignItems="center" spacing={2}>
<Button variant="contained" onClick={() => createprofile()}>
Create
</Button>
</Stack>
</Grid>
</Grid>
</MainCard>
</>
);
};
export default Createclient;

View File

@@ -0,0 +1,693 @@
import { React, useState, useEffect, useRef, useMemo } from 'react';
import { Empty } from 'antd';
import axios from 'axios';
import { FaRegEdit } from 'react-icons/fa';
import { RiEdit2Fill } from 'react-icons/ri';
import { useTheme } from '@mui/material/styles';
import LoaderWithImage from 'components/nearle_components/LoaderWithImage';
// material-ui
import {
Box,
Divider,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Grid,
Typography,
IconButton,
Stack,
Tooltip,
Dialog,
DialogActions,
DialogTitle,
DialogContent,
Button,
TextField,
Autocomplete
} from '@mui/material';
import Geocode from 'react-geocode';
import LocationOnIcon from '@mui/icons-material/LocationOn';
import parse from 'autosuggest-highlight/parse';
import { debounce } from '@mui/material/utils';
// project imports
import MainCard from 'components/MainCard';
import Loader from 'components/Loader';
import DebounceSearchBar from 'components/nearle_components/DebounceSearchBar';
import LocationAutocomplete from 'components/nearle_components/LocationAutocomplete';
import { useInfiniteQuery, useQuery } from '@tanstack/react-query';
import { getallcustomers, getcustomersummary } from 'pages/api/api';
import { OpenToast } from 'components/third-party/OpenToast';
import { OrdersTableSkeleton } from '../orders/OrdersTableSkeleton';
// ==============================|| google address ||============================== //
const GOOGLE_MAPS_API_KEY = process.env.REACT_APP_GOOGLE_MAPS_API_KEY;
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 autocompleteService = { current: null };
// ==============================|| MUI TABLE - ENHANCED ||============================== //
export default function Customers() {
const containerRef = useRef();
const loadMoreRef = useRef();
const [rowsPerPage] = useState(50);
const [page] = useState(0);
const theme = useTheme();
const [appId, setAppId] = useState(0);
const [locaName, setLocoName] = useState('All');
const [selectedCustomer, setSelectedCustomer] = useState({}); // to edit
const [open, setOpen] = useState(false);
const [address, setAddress] = useState('');
const [latlong, setLatlong] = useState({});
const [city, setCity] = useState('');
const [postcode, setPostcode] = useState('');
const [state, setState] = useState('');
const [suburb, setSuburb] = useState('');
const [searchword, setSearchword] = useState('');
const [debouncedSearch, setDebouncedSearch] = useState('');
// ==============================|| for google address ||============================== //
const [value, setValue] = useState(null);
const [inputValue, setInputValue] = useState('');
const [options, setOptions] = useState([]);
const loaded = useRef(false);
if (typeof window !== 'undefined' && !loaded.current) {
if (!document.querySelector('#google-maps')) {
loadScript(
`https://maps.googleapis.com/maps/api/js?key=${GOOGLE_MAPS_API_KEY}&libraries=places`,
document.querySelector('head'),
'google-maps'
);
}
loaded.current = true;
}
const fetch = useMemo(
() =>
debounce((request, callback) => {
autocompleteService.current.getPlacePredictions(request, callback);
}, 400),
[]
);
useEffect(() => {
let active = true;
if (!autocompleteService.current && window.google) {
autocompleteService.current = new window.google.maps.places.AutocompleteService();
}
if (!autocompleteService.current) {
return undefined;
}
if (inputValue === '') {
setOptions(value ? [value] : []);
return undefined;
}
fetch({ input: inputValue }, (results) => {
if (active) {
let newOptions = [];
if (value) {
newOptions = [value];
}
if (results) {
newOptions = [...newOptions, ...results];
}
setOptions(newOptions);
}
});
return () => {
active = false;
};
}, [value, inputValue, fetch]);
Geocode.setApiKey(process.env.REACT_APP_GOOGLE_MAPS_API_KEY);
useEffect(() => {
try {
console.log('selected address =>', address);
Geocode.fromAddress(address).then(
(response) => {
console.log('lat long response =>', response.results[0]);
if (response.status == 'OK') {
const { lat, lng } = response.results[0].geometry.location;
setLatlong({
lat,
lng
});
// setSelectedCustomer({
// ...selectedCustomer,
// latitude: lat,
// longitude: lng
// });
if (response.results[0].address_components) {
let place = response.results[0];
let city1, zipcode1, state1, suburb1;
for (let i = 0; i < place.address_components.length; i++) {
for (let j = 0; j < place.address_components[i].types.length; j++) {
switch (place.address_components[i].types[j]) {
case 'locality':
city1 = place.address_components[i].long_name;
break;
case 'administrative_area_level_1':
state1 = place.address_components[i].long_name;
break;
case 'postal_code':
zipcode1 = place.address_components[i].long_name;
break;
case 'sublocality':
suburb1 = place.address_components[i].long_name;
break;
}
}
}
setCity(city1 || '');
setState(state1 || '');
setPostcode(zipcode1 || '');
setSuburb(suburb1 || '');
setSelectedCustomer((prev) => ({
...prev,
city: city1 || '',
state: state1 || '',
postcode: zipcode1 || '',
suburb: suburb1 || '',
latitude: lat || '',
longitude: lng || ''
}));
}
}
},
(error) => {
console.log(error);
}
);
} catch (err) {
console.log(err);
}
}, [address]);
// useEffect(() => {
// selectedCustomer &&
// setLatlong({
// lat: selectedCustomer.latitude,
// lng: selectedCustomer.longitude
// });
// }, [selectedCustomer]);
// ==============================|| getallcustomers (customers) ||============================== //
const {
data,
isLoading: customersIsLoading,
isFetchingNextPage,
fetchNextPage,
hasNextPage,
refetch: getallcustomersRefetch
} = useInfiniteQuery({
queryKey: ['getAllCustomers', appId, debouncedSearch, rowsPerPage],
queryFn: getallcustomers,
getNextPageParam: (lastPage) => lastPage.nextPage,
keepPreviousData: true
});
const rows = data?.pages.flatMap((page) => page.data) || [];
useEffect(() => {
if (!hasNextPage) return;
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting) {
fetchNextPage();
}
},
{
root: document.querySelector('.MuiTableContainer-root'), // 👈 or explicitly TableContainer
rootMargin: '0px',
threshold: 1.0
}
);
if (loadMoreRef.current) observer.observe(loadMoreRef.current);
return () => {
if (loadMoreRef.current) observer.unobserve(loadMoreRef.current);
};
}, [hasNextPage, fetchNextPage]);
const handleScroll = (event) => {
const { scrollTop, scrollHeight, clientHeight } = event.currentTarget;
if (scrollTop + clientHeight >= scrollHeight - 50) {
if (hasNextPage && !isFetchingNextPage) {
fetchNextPage();
}
}
};
// ==============================|| getcustomersummary (customers) ||============================== //
const { data: pageCount, isLoading: customerSummaryIsLoading } = useQuery({
queryKey: ['customersummary', appId],
queryFn: getcustomersummary
});
useEffect(() => {
console.log('pageCount', pageCount);
}, [pageCount]);
// ==============================|| updateCustomer (post)||============================== //
const updateCustomer = async () => {
console.log('selectedCustomer', selectedCustomer);
if (!selectedCustomer.firstname) {
OpenToast('Enter Door NO', 'warning', 1500);
} else if (!selectedCustomer.contactno) {
OpenToast('Enter Contact Number ', 'warning', 1500);
} else if (!selectedCustomer.address) {
OpenToast('Enter Valid Address', 'warning', 1500);
} else if (!selectedCustomer.suburb) {
OpenToast('Enter Suburb', 'warning', 1500);
} else if (!selectedCustomer.city) {
OpenToast('Enter City ', 'warning', 1500);
} else if (!selectedCustomer.state) {
OpenToast('Enter State', 'warning', 1500);
} else if (!selectedCustomer.postcode) {
OpenToast('Enter PostCode', 'warning', 1500);
} else if (!selectedCustomer.landmark) {
OpenToast('Enter Landmark', 'warning', 1500);
} else if (!selectedCustomer.latitude) {
OpenToast('Enter Latitude', 'warning', 1500);
} else if (!selectedCustomer.longitude) {
OpenToast('Enter Longitude', 'warning', 1500);
} else {
try {
const postUpdateResponse = await axios.put(`${process.env.REACT_APP_URL}/customers/update`, {
customerid: selectedCustomer.customerid,
configid: 1,
firstname: selectedCustomer.firstname,
applocationid: selectedCustomer.applocationid,
profileimage: '',
dialcode: '+91',
contactno: selectedCustomer.contactno,
devicetype: '',
deviceid: '',
customertoken: '123',
address: selectedCustomer.address,
suburb: suburb,
city: city,
state: state,
postcode: postcode,
landmark: selectedCustomer.landmark,
doorno: selectedCustomer.doorno,
latitude: selectedCustomer.latitude.toString(),
longitude: selectedCustomer.longitude.toString()
});
console.log('postUpdateResponse', postUpdateResponse);
if (postUpdateResponse.data.status) {
OpenToast(postUpdateResponse.data.message, 'success', 1500);
setOpen(false);
getallcustomersRefetch();
}
} catch (error) {
console.log('postUpdate error', error);
}
}
};
return (
<>
{(customerSummaryIsLoading || customersIsLoading) && (
<>
{/* <CircularLoader /> */}
<Loader />
</>
)}
{/* <TitleCard title={'Customers'} /> */}
<MainCard
content={false}
sx={{}}
title={
<Stack sx={{ m: 2 }}>
<Stack
direction={{ xs: 'column', sm: 'row' }}
alignItems={{ xs: 'flex-start', sm: 'center' }}
justifyContent="space-between"
spacing={2}
sx={{ width: '100%' }}
>
{/* Left: Title */}
<Typography variant="h3">Customers</Typography>
{/* Right: Controls */}
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={2} alignItems="center" width={{ xs: '100%', sm: 'auto' }}>
<LocationAutocomplete
locaName={locaName}
setAppId={setAppId}
setLocoName={setLocoName}
sx={{
width: { xs: '100%', sm: 320 },
zIndex: 100
}}
/>
<DebounceSearchBar
value={searchword}
onChange={setSearchword}
onDebouncedChange={setDebouncedSearch}
placeholder={`Search ${pageCount?.Total || ''} Customer (ctrl+k)`}
sx={{
width: { xs: '100%', sm: 320 }
}}
/>
</Stack>
</Stack>
</Stack>
}
>
<TableContainer ref={containerRef} onScroll={handleScroll} sx={{ maxHeight: 'calc(100vh - 180px)' }}>
<Table stickyHeader>
<TableHead>
<TableRow>
<TableCell sx={{ position: 'sticky !important' }}>#</TableCell>
<TableCell sx={{ position: 'sticky !important' }}>Name</TableCell>
<TableCell sx={{ position: 'sticky !important' }}>Contact</TableCell>
<TableCell sx={{ position: 'sticky !important' }}>Address</TableCell>
<TableCell sx={{ position: 'sticky !important' }}>Location</TableCell>
<TableCell sx={{ position: 'sticky !important' }}>Action</TableCell>
</TableRow>
</TableHead>
<TableBody>
{customersIsLoading && <OrdersTableSkeleton />}
{rows?.length == 0 && !customersIsLoading ? (
<TableRow>
<TableCell colSpan={6}>
<Stack width={'100%'} direction={'row'} justifyContent={'center'}>
<Empty />
</Stack>
</TableCell>
</TableRow>
) : (
rows?.map((row, index) => {
return (
<TableRow key={row.name}>
<TableCell padding="none">{index + 1 + page * rowsPerPage}</TableCell>
<TableCell align="left">
<Typography align="left" variant="subtitle1">
{row.firstname}
</Typography>
<Typography align="left" variant="caption" color="secondary">
Id : {row.customerid}
</Typography>
</TableCell>
<TableCell align="left">
<Typography align="left" variant="subtitle1">
{row.contactno}
</Typography>
<Typography align="left" variant="caption" color="secondary">
{row.email}
</Typography>
</TableCell>
<TableCell align="left">{row.address}</TableCell>
<TableCell align="left">{row.suburb}</TableCell>
<TableCell align="center">
<Tooltip title={'To Edit'}>
<IconButton
onClick={() => {
console.log('row', row);
setSelectedCustomer(row);
setTimeout(() => {
setOpen(true);
}, 0);
}}
>
<RiEdit2Fill
fontSize={'large'}
style={{
cursor: 'pointer',
color: theme.palette.primary.main
}}
/>
</IconButton>
</Tooltip>
</TableCell>
</TableRow>
);
})
)}
{rows?.length != 0 && (
<TableRow>
<TableCell colSpan={15} rowSpan={3}>
<div ref={loadMoreRef} style={{ height: 40, textAlign: 'center' }}>
{isFetchingNextPage ? <LoaderWithImage /> : hasNextPage ? <LoaderWithImage /> : 'No More Orders'}
</div>
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</TableContainer>
<Divider />
</MainCard>
{/* ======================================== || Edit Dialog || ======================================== */}
<Dialog
open={open}
onClose={() => setOpen(false)}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
maxWidth="lg"
fullWidth
>
<DialogTitle id="alert-dialog-title" sx={{ bgcolor: theme.palette.primary.main, color: 'white' }}>
<Stack direction={'row'} alignItems={'center'} spacing={1}>
<FaRegEdit style={{ fontSize: 25 }} /> <Typography variant="h3">Edit Customer</Typography>
</Stack>
</DialogTitle>
<DialogContent>
<Grid container spacing={2} sx={{ mt: 2 }}>
<Grid item xs={6}>
<Typography sx={{ mb: 1 }}>Customer Name</Typography>
<TextField
variant="outlined"
fullWidth
defaultValue={selectedCustomer?.firstname}
onChange={(e) => {
setSelectedCustomer({
...selectedCustomer,
firstname: e.target.value
});
}}
/>
</Grid>
<Grid item xs={6}>
<Typography sx={{ mb: 1 }}>Contact Number</Typography>
<Stack direction={'row'} spacing={1}>
<TextField readonly variant="outlined" value={'+91'} sx={{ width: 60 }} />
<TextField
variant="outlined"
fullWidth
type="text"
value={selectedCustomer?.contactno || ''}
inputProps={{
maxLength: 10,
inputMode: 'numeric', // mobile numeric keypad
pattern: '[0-9]*'
}}
onChange={(e) => {
const value = e.target.value.replace(/\D/g, ''); // allow only digits
setSelectedCustomer((prev) => ({
...prev,
contactno: value
}));
}}
/>
</Stack>
</Grid>
<Grid item xs={12}>
<Typography sx={{ mb: 1 }}>Address</Typography>
<Autocomplete
id="google-map-demo"
sx={{}}
fullWidth
getOptionLabel={(option) => (typeof option === 'string' ? option : option?.description || '')}
filterOptions={(x) => x}
options={options}
autoComplete
includeInputInList
filterSelectedOptions
value={selectedCustomer?.address}
noOptionsText="No locations"
onChange={(event, newValue) => {
setOptions(newValue ? [newValue, ...options] : options);
setValue(newValue);
console.log('newValue', newValue || '');
setAddress(newValue?.description);
setSelectedCustomer({
...selectedCustomer,
address: newValue?.description
});
}}
onInputChange={(event, newInputValue) => {
setInputValue(newInputValue);
}}
renderInput={(params) => <TextField {...params} fullWidth />}
renderOption={(props, option) => {
const matches = option.structured_formatting.main_text_matched_substrings || [];
const parts = parse(
option.structured_formatting.main_text,
matches.map((match) => [match.offset, match.offset + match.length])
);
return (
<li {...props}>
<Grid container alignItems="center">
<Grid item sx={{ display: 'flex', width: 44 }}>
<LocationOnIcon sx={{ color: 'text.secondary' }} />
</Grid>
<Grid
item
sx={{
width: 'calc(100% - 44px)',
wordWrap: 'break-word'
}}
>
{parts?.map((part, index) => (
<Box
key={index}
component="span"
sx={{
fontWeight: part.highlight ? 'bold' : 'regular'
}}
>
{part.text}
</Box>
))}
<Typography variant="body2" color="text.secondary">
{option?.structured_formatting.secondary_text}
</Typography>
</Grid>
</Grid>
</li>
);
}}
/>
</Grid>
<Grid item xs={6}>
<Typography sx={{ mb: 1 }}>Location</Typography>
<TextField
variant="outlined"
fullWidth
value={selectedCustomer?.suburb}
onChange={(e) => {
const value = e.target.value;
setSelectedCustomer((prev) => ({
...prev,
suburb: value
}));
// setSuburb(e.target.value);
}}
/>
</Grid>
<Grid item xs={6}>
<Typography sx={{ mb: 1 }}>City</Typography>
<TextField
variant="outlined"
fullWidth
value={selectedCustomer.city || city || ''}
onChange={(e) => {
const value = e.target.value;
setSelectedCustomer((prev) => ({
...prev,
city: value
}));
// setCity(e.target.value);
}}
/>
</Grid>
<Grid item xs={6}>
<Typography sx={{ mb: 1 }}>State</Typography>
<TextField
variant="outlined"
fullWidth
value={selectedCustomer.state || state || ''}
onChange={(e) => {
const value = e.target.value;
setSelectedCustomer((prev) => ({
...prev,
state: value
}));
// setState(e.target.value);
}}
/>
</Grid>
<Grid item xs={6}>
<Typography sx={{ mb: 1 }}>Postcode</Typography>
<TextField
variant="outlined"
fullWidth
value={postcode == '' ? selectedCustomer.postcode : postcode}
onChange={(e) => {
const value = e.target.value;
setSelectedCustomer((prev) => ({
...prev,
postcode: value
}));
// setPostcode(e.target.value);
}}
/>
</Grid>
<Grid item xs={12}>
<Typography sx={{ mb: 1 }}>Landmark</Typography>
<TextField
variant="outlined"
fullWidth
defaultValue={selectedCustomer.landmark}
onChange={(e) => {
setSelectedCustomer({
...selectedCustomer,
landmark: e.target.value
});
}}
/>
</Grid>
<Grid item xs={6} sx={{ cursor: 'not-allowed' }}>
<Typography sx={{ mb: 1 }}>Latitude</Typography>
<TextField variant="outlined" fullWidth value={latlong.lat} sx={{ cursor: 'not-allowed' }} />
</Grid>
<Grid item xs={6} sx={{ cursor: 'not-allowed' }}>
<Typography sx={{ mb: 1 }}>Longitude</Typography>
<TextField readonly variant="outlined" fullWidth value={latlong.lng} />
</Grid>
</Grid>
</DialogContent>
<DialogActions sx={{ mr: 2, mb: 2 }}>
<Button onClick={() => setOpen(false)} variant="contained" color="error" sx={{ mr: 2 }}>
Close
</Button>
<Button
onClick={() => {
updateCustomer();
}}
variant="contained"
autoFocus
>
Update
</Button>
</DialogActions>
</Dialog>
</>
);
}

View File

@@ -0,0 +1,9 @@
const Dashboard = () => {
return (
<>
<h1>Dashboard</h1>
</>
);
};
export default Dashboard;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,419 @@
import React from 'react';
import { useState } from 'react';
import TitleCard from 'pages/titleCard';
import HoverSocialCard from 'components/cards/statistics/HoverSocialCard';
import { useTheme } from '@mui/material/styles';
import { Outlet, useNavigate } from 'react-router-dom';
import dayjs from 'dayjs';
var utc = require('dayjs/plugin/utc');
dayjs.extend(utc);
import { DashboardOutlined } from '@ant-design/icons';
import { HiHandThumbDown, HiHandThumbUp } from 'react-icons/hi2';
import LoadingIcons from 'react-loading-icons';
import { useQuery } from '@tanstack/react-query';
import { fetchinvoiceinsight, fetchdeliverylist } from 'pages/api/api';
import VisibilityIcon from '@mui/icons-material/Visibility';
import {
Grid,
Divider,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TablePagination,
TableRow,
Tabs,
Tab,
Typography,
Box,
IconButton,
Tooltip,
Stack
} from '@mui/material';
import Loader from 'components/Loader';
import MainCard from 'components/MainCard';
import { Empty } from 'antd';
import DateFilterDialog from 'components/DateFilterDialog';
import { OrdersTableSkeleton } from '../orders/OrdersTableSkeleton';
const columns = [
{ id: 'sno', label: 'sno' },
{ id: 'client', label: 'client' },
{ id: 'invoiceid', label: 'Invoice Id' },
{
id: 'invoice date',
label: 'invoice date',
align: 'left'
// format: (value) => value.toLocaleString("en-US"),
},
{
id: 'due date',
label: 'due date',
align: 'left'
// format: (value) => value.toLocaleString("en-US"),
},
{
id: 'itemcount',
label: 'Count',
align: 'left'
// format: (value) => typeof value === "number" && value.toFixed(2),
},
{ id: 'amount', label: 'amount', align: 'right' },
{ id: 'action', label: 'action', align: 'center' }
];
// table data
function CustomTabPanel(props) {
const { children, value, index, ...other } = props;
return (
<div role="tabpanel" hidden={value !== index} id={`simple-tabpanel-${index}`} aria-labelledby={`simple-tab-${index}`} {...other}>
{value === index && (
<Box sx={{ p: 3 }}>
<Typography>{children}</Typography>
</Box>
)}
</div>
);
}
function a11yProps(index) {
return {
id: `simple-tab-${index}`,
'aria-controls': `simple-tabpanel-${index}`
};
}
function formatNumberToRupees(value) {
return new Intl.NumberFormat('en-IN', {
style: 'currency',
currency: 'INR',
minimumFractionDigits: 2
}).format(value);
}
const Invoice = () => {
const theme = useTheme();
const navigate = useNavigate();
const [page, setPage] = React.useState(0);
const [rowsPerPage, setRowsPerPage] = React.useState(10);
const [value, setValue] = React.useState(0);
const [billStatus, setBillStatus] = useState(0);
let [isloader, setIsLoader] = useState(false);
const [open, setOpen] = useState(false);
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(+event?.target?.value);
setPage(0);
};
const handleChange = (event, newValue) => {
setValue(newValue);
};
/* ============================================= || fetchinvoiceinsight| ============================================= */
const {
data: insightdata,
isLoading: isInsightLoading,
isError: isInsightError,
error: insightError
} = useQuery({
queryKey: ['invoiceInsight'], // Meaningful query key
queryFn: fetchinvoiceinsight,
refetchInterval: 300000 // Auto-fetch every 5 minutes
});
/* ============================================= || fetchdeliverylist| ============================================= */
const {
data: deliveryList,
isLoading: isDeliveryLoading,
isError: isDeliveryError,
error: deliveryError
} = useQuery({
queryKey: [billStatus], // Include billStatus in query key
queryFn: fetchdeliverylist,
refetchInterval: 300000 // Auto-fetch every 5 minutes
});
const isLoading = isInsightLoading || isDeliveryLoading;
const isError = isInsightError || isDeliveryError;
const errorMessage = insightError?.message || deliveryError?.message;
if (isError) {
return errorMessage;
}
return (
<>
{(isloader || isLoading) && <Loader />}
{/* ============================================= || TitleCard || ============================================= */}
<TitleCard title="Invoice" />
{/* ============================================= || Hover Social Card || ============================================= */}
<Grid container spacing={3}>
<Grid
item
xs={12}
lg={3}
sm={6}
sx={{ cursor: 'pointer' }}
onClick={() => {
setValue(0);
setBillStatus(0);
}}
>
<HoverSocialCard
primary="All"
secondary={insightdata?.totalcount}
iconPrimary={DashboardOutlined}
color={theme.palette.primary.main}
/>
</Grid>
<Grid
item
xs={12}
lg={3}
sm={6}
sx={{ cursor: 'pointer' }}
onClick={() => {
setValue(1);
setBillStatus(1);
}}
>
<HoverSocialCard
primary="0pen"
secondary={insightdata?.pendingcount}
iconPrimary={LoadingIcons.ThreeDots}
color={theme.palette.error.main}
/>
</Grid>
<Grid
item
xs={12}
lg={3}
sm={6}
sx={{ cursor: 'pointer' }}
onClick={() => {
setValue(2);
setBillStatus(2);
}}
>
<HoverSocialCard
primary="Overdue"
secondary={insightdata?.overduecount}
iconPrimary={HiHandThumbDown}
color={theme.palette.warning.main}
/>
</Grid>
<Grid
item
xs={12}
lg={3}
sm={6}
sx={{ cursor: 'pointer' }}
onClick={() => {
setValue(3);
setBillStatus(3);
}}
>
<HoverSocialCard
primary="Paid"
secondary={insightdata?.paidcount}
color={theme.palette.success.main}
iconPrimary={HiHandThumbUp}
/>
</Grid>
</Grid>
{/* ============================================= || Invoice Table || ============================================= */}
<Stack sx={{ border: '1px solid', borderColor: 'bg.main', p: 3, mt: 3 }}>
<Grid container sx={{}}>
<Grid item xs={9} sx={{}}>
<Tabs value={value} onChange={handleChange} aria-label="basic tabs example" sx={{ mt: -1, mb: -2 }}>
<Tab
label="All"
{...a11yProps(0)}
onClick={() => {
setValue(0);
setBillStatus(0);
}}
/>
<Tab
label="Open"
{...a11yProps(1)}
onClick={() => {
setValue(1);
setBillStatus(1);
}}
/>
<Tab
label="Overdue"
{...a11yProps(2)}
onClick={() => {
setValue(2);
setBillStatus(2);
}}
/>
<Tab
label="Paid"
{...a11yProps(3)}
onClick={() => {
setValue(3);
setBillStatus(3);
}}
/>
</Tabs>
</Grid>
</Grid>
</Stack>
<MainCard content={false}>
<CustomTabPanel value={value} index={value}>
<TableContainer
sx={{
// maxHeight: 430,
// maxHeight: '55vh',
mt: -3,
'&::-webkit-scrollbar': {
width: '4px', // Width of vertical scrollbar
height: '4px' // Height of horizontal scrollbar
},
'&::-webkit-scrollbar-thumb': {
backgroundColor: '#65387A' // Color of the scrollbar thumb
}
}}
>
<Table stickyHeader aria-label="sticky table" sx={{}}>
<TableHead
sx={{
'& th': {
borderTop: `1px solid ${theme.palette.divider}`,
borderBottom: `2px solid ${theme.palette.divider} !important`
}
}}
>
<TableRow>
{columns.map((column) => (
<TableCell
sx={{
minWidth: column.minWidth,
position: 'sticky !important'
}}
key={column.id}
align={column.align}
>
{column.label}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{isDeliveryLoading && <OrdersTableSkeleton col={3} />}
{deliveryList?.length == 0 ? (
<TableRow>
<TableCell colSpan={8}>
<Empty />
</TableCell>
</TableRow>
) : (
deliveryList?.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage).map((item, index) => (
<TableRow key={index}>
<TableCell>{page * rowsPerPage + index + 1}</TableCell>
<TableCell>
<Typography>{item.tenantname} </Typography>
<Typography variant="caption" color={'secondary'}>
{item.contactperson}{' '}
</Typography>
</TableCell>
<TableCell>
<Typography variant="caption">{item.invoiceno} </Typography>
</TableCell>
<TableCell>
<stack direction={'column'}>
<Typography variant="caption">{dayjs(item.transactiondate).format('DD-MM-YYYY')}</Typography>
<br />
<Typography variant="caption">{dayjs(item.transactiondate).utc().format('hh:mm a')}</Typography>
</stack>
</TableCell>
<TableCell>
<stack direction={'column'}>
<Typography variant="caption">{dayjs(item.duedate).format('DD-MM-YYYY')}</Typography>
<br />
<Typography variant="caption">{dayjs(item.duedate).utc().format('hh:mm a')}</Typography>
</stack>
</TableCell>
<TableCell>
{' '}
<Typography variant="caption">{item.itemcount} </Typography>
</TableCell>
<TableCell align="right">
<Typography variant="caption">{formatNumberToRupees(item.totalamount)}</Typography>
</TableCell>
<TableCell align="center">
<Tooltip title="Preview" placement="right">
<IconButton
sx={{
'&:hover': {
color: theme.palette.primary.main
}
}}
onClick={() => {
setIsLoader(true);
console.log('selected', item);
setTimeout(() => {
setIsLoader(false);
navigate('/nearle/invoice/preview', {
state: item
});
}, 500);
}}
>
<VisibilityIcon fontSize="small" />
</IconButton>
</Tooltip>
</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
</TableContainer>
</CustomTabPanel>
<Divider />
{/* table pagination */}
<TablePagination
rowsPerPageOptions={[5, 10, 25, 100]}
component="div"
count={deliveryList?.length}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
/>
</MainCard>
{/* ============================================= || Date Dialog|| ============================================= */}
<DateFilterDialog
open={open}
onClose={() => setOpen(false)}
onSelect={(range) => {
setStartdate(range.startDate);
setEnddate(range.endDate);
setDatestatus(range.label);
console.log('Selected Date Range:', range);
}}
/>
<Outlet />
</>
);
};
export default Invoice;

View File

@@ -0,0 +1,472 @@
import React, { useRef, useState, useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import { useTheme } from '@mui/material/styles';
// import nearleLogo from '../../../assets/images/nearleLogo.png';
import logo_nearle1 from '../../../assets/images/logo-nearle1.png';
import axios from 'axios';
import dayjs from 'dayjs';
import Loader from 'components/Loader';
import { enqueueSnackbar } from 'notistack';
import { DownloadOutlined, PrinterFilled } from '@ant-design/icons';
import ReactToPrint, { useReactToPrint } from 'react-to-print';
import { SearchOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
// import jsPDF from 'jspdf';
import { useNavigate } from 'react-router-dom';
import { FaArrowLeft } from 'react-icons/fa6';
import { FaIndianRupeeSign } from 'react-icons/fa6';
// import autoTable from 'jspdf-autotable';
import {
Grid,
Button,
Divider,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TablePagination,
TableRow,
Tabs,
Tab,
Typography,
Box,
OutlinedInput,
InputAdornment,
IconButton,
TextField,
Tooltip,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Stack,
Chip
} from '@mui/material';
const InvoicePreview = () => {
const [selected, setselected] = useState({});
const location = useLocation();
const navigate = useNavigate();
console.log('previewSelect', location.state);
const componentRef = useRef(null);
const [tabletype, settabletype] = useState(true);
const [paydialog, setpaydialog] = useState(false);
const [refnumber, setRefnumber] = useState('');
const [remarks, setRemarks] = useState('');
const theme = useTheme();
useEffect(() => {
setselected(location.state);
}, []);
// ================================================= || formatNumberToRupees || =================================================
function formatNumberToRupees(value) {
return new Intl.NumberFormat('en-IN', {
style: 'currency',
currency: 'INR',
minimumFractionDigits: 2
}).format(value);
}
// ================================================= || updatePayment || =================================================
const updatePayment = async () => {
try {
const updateResponse = await axios.put(`${process.env.REACT_APP_URL}/invoice/updatestatus`, {
salesid: selected.salesid,
referenceno: refnumber,
referencedate: dayjs().format('YYYY-MM-DD HH:mm:ss'),
billstatus: 2,
paymentremarks: remarks
});
if (updateResponse.status) {
enqueueSnackbar(' Updated Successfully ', {
variant: 'success',
anchorOrigin: { vertical: 'top', horizontal: 'right' },
autoHideDuration: 1000
});
}
console.log('updateResponse', updateResponse);
} catch (error) {
console.log('updateResponse', error);
}
};
return (
<>
<Stack direction="row" justifyContent="Space-between" alignItems={'center'} spacing={2} sx={{ px: 2.5, py: 1, bgcolor: '#eeeeee' }}>
<Stack direction={'row'} alignItems={'center'} spacing={2}>
<Tooltip title="back">
<IconButton
color="primary"
onClick={() => {
navigate('/nearle/invoice');
}}
>
<FaArrowLeft size={'large'} />
</IconButton>
</Tooltip>
<Stack alignItems={'center'}>
<Typography variant="h3" color={'primary'}>
Invoice Details
</Typography>
<Chip
size="small"
color="warning"
variant="outlined"
sx={{ bgcolor: theme.palette.warning.lighter }}
label={`Invoice No :${'\u00a0\u00a0'}${selected.invoiceno}`}
/>
</Stack>
</Stack>
<Stack direction={'row'} spacing={2}>
<Button
variant="outlined"
color="primary"
sx={{
'&:hover': {
backgroundColor: 'primary.main',
color: 'primary.contrastText'
}
}}
onClick={() => {
setpaydialog(true);
}}
>
{' '}
<FaIndianRupeeSign />
Update Payment
</Button>
<ReactToPrint
trigger={() => (
<Button
size="small"
startIcon={<PrinterFilled />}
variant="outlined"
color="primary"
sx={{
'&:hover': {
backgroundColor: 'primary.main',
color: 'primary.contrastText'
}
}}
>
Print
</Button>
)}
content={() => componentRef.current}
/>
</Stack>
</Stack>
<Box sx={{ pb: 2.5, border: '1px solid #eee' }}>
<div ref={componentRef} style={{ width: '100%' }}>
<Box id="print" sx={{ p: 2.5 }}>
<Box sx={{ pb: 2.5 }}>
<Stack
sx={{
flexDirection: 'row',
// bgcolor: theme.palette.primary.main,
border: '1px solid #eee',
px: 3
}}
justifyContent="space-between"
>
<Box sx={{ pt: 0.5 }}>
<Stack direction="row" spacing={2}>
<img src={logo_nearle1} style={{ width: '150px', height: '50px' }} />{' '}
</Stack>
{/* <Typography
sx={{ color: theme.palette.primary.main, py: 0.5 }}
>
{`Invoice No: ${"\u00a0\u00a0\u00a0"}${selected.invoiceno}`}
</Typography> */}
<Stack direction="row" justifyContent="space-between">
<Typography
sx={{
overflow: 'hidden',
color: theme.palette.primary.main
}}
variant="subtitle1"
>
Invoice No :
</Typography>
<Typography sx={{ color: theme.palette.primary.main }}>{`${'\u00a0\u00a0'}${selected.invoiceno}`}</Typography>
</Stack>
</Box>
<Box sx={{ pt: 2.5, pb: 1.75 }}>
<Stack direction="row" justifyContent="space-between">
<Typography sx={{ pl: 4, color: theme.palette.primary.main }} variant="subtitle1">
Date :{' '}
</Typography>
<Typography sx={{ color: theme.palette.primary.main }}>
{dayjs(selected.transactiondate).format('DD-MM-YYYY')}
</Typography>
</Stack>
<Stack direction="row" justifyContent="space-between">
<Typography
sx={{
pr: 2,
overflow: 'hidden',
color: theme.palette.primary.main
}}
variant="subtitle1"
>
Due Date :
</Typography>
<Typography sx={{ color: theme.palette.primary.main }}>{dayjs(selected.dueDate).format('DD-MM-YYYY')}</Typography>
</Stack>
{/* <Stack direction="row" justifyContent="space-between">
<Typography
sx={{
pr: 2,
overflow: "hidden",
color: theme.palette.primary.main,
}}
variant="subtitle1"
>
Invoice No :
</Typography>
<Typography sx={{ color: theme.palette.primary.main }}>
{`${"\u00a0\u00a0\u00a0"}${selected.invoiceno}`}
</Typography>
</Stack> */}
</Box>
</Stack>
<Box sx={{ pt: 2.5 }}>
<Grid container spacing={2} justifyContent="space-between" direction="row">
<Grid item xs={12} sm={6}>
<Box
sx={{
border: 1,
minHeight: 240,
borderColor: 'grey.200',
borderRadius: 0.5,
p: 2.5
}}
>
<Grid container direction="row">
<Grid item md={8}>
<Stack spacing={2}>
<Typography variant="h5">From:</Typography>
<Stack sx={{ width: '100%' }}>
<Typography variant="subtitle1">Nearle Technology Privite Limited.</Typography>
<Typography color="secondary">
424, 4<sup>th</sup>floor,
</Typography>
<Typography color="secondary">Red rose towers,</Typography>
<Typography color="secondary">DB Road, RS Puram,</Typography>
<Typography color="secondary">641002.</Typography>
<Typography color="secondary">care@nearle.in</Typography>
<Typography color="secondary">9047968666</Typography>
</Stack>
</Stack>
</Grid>
</Grid>
</Box>
</Grid>
<Grid item xs={12} sm={6}>
<Box
sx={{
border: 1,
minHeight: 240,
borderColor: 'grey.200',
borderRadius: 0.5,
p: 2.5
}}
>
<Grid container direction="row">
<Grid item md={8}>
<Stack spacing={2}>
<Typography variant="h5">To:</Typography>
<Stack sx={{ width: '100%' }}>
<Typography variant="subtitle1">{selected.tenantname}</Typography>
<Typography color="secondary">{selected.address}</Typography>
<Typography color="secondary">{selected.suburb}</Typography>
<Typography color="secondary">{selected.city}</Typography>
<Typography color="secondary">{selected.state}</Typography>{' '}
</Stack>
</Stack>
</Grid>
</Grid>
</Box>
</Grid>
</Grid>
</Box>
</Box>
<TableContainer>
<Table>
<TableHead>
<TableRow>
<TableCell>S.No</TableCell>
<TableCell>Particulars</TableCell>
<TableCell>Unit</TableCell>
<TableCell>Quantity</TableCell>
<TableCell align="right">Rate</TableCell>
{/* {selected && selected.pricingtypeid === 73 && ( */}
<TableCell align="right">Other Charges</TableCell>
{/* )} */}
<TableCell align="right">Amount</TableCell>
</TableRow>
</TableHead>
{selected.tenantsalesdetails && (
<TableBody>
<TableRow>
<TableCell>1</TableCell>
<TableCell>
<Typography>
{`Invoice from ${dayjs(selected.tenantsalesdetails[0].fromdate).format('DD-MM-YYYY')} to ${dayjs(
selected.tenantsalesdetails[0].todate
).format('DD-MM-YYYY')}`}
</Typography>
</TableCell>
<TableCell>
<Typography>{selected.tenantsalesdetails[0].pricingtype}</Typography>
</TableCell>
<TableCell>
<Typography>{`${selected.tenantsalesdetails[0].quantity.toFixed(2)} km`}</Typography>
</TableCell>
<TableCell>
<Typography align="right">{`${selected.tenantsalesdetails[0].baserate.toFixed(2)}`}</Typography>
</TableCell>
{/* {selected.tenantsalesdetails[0].pricingtypeid == 73 && ( */}
<TableCell align="right">
<Typography>{`${selected.tenantsalesdetails[0].othercharges}.00`}</Typography>
</TableCell>
{/* )} */}
<TableCell align="right">
<Typography>{`${selected.tenantsalesdetails[0].amount}.00`}</Typography>
</TableCell>
</TableRow>
</TableBody>
)}
</Table>
</TableContainer>
<Divider />
<Box sx={{ p: 2.5 }}>
<Grid container direction="row" justifyContent="flex-end">
<Grid item md={4}>
<Stack spacing={2}>
<Stack direction="row" justifyContent="space-between">
<Typography color="secondary">Sub Total:</Typography>
<Typography variant="h6">{formatNumberToRupees(selected.salesamount)}</Typography>
</Stack>
<Stack direction="row" justifyContent="space-between">
<Typography color="secondary">Discount:</Typography>
<Typography variant="h6" color={theme.palette.error.main}>
- {formatNumberToRupees(selected.discountamt)}
</Typography>
</Stack>
<Stack direction="row" justifyContent="space-between">
<Typography color={theme.palette.grey[500]}>Tax:</Typography>
<Typography variant="h6" color={theme.palette.success.main}>
+ {formatNumberToRupees(selected.taxamount)}
</Typography>
</Stack>
<Stack direction="row" justifyContent="space-between">
<Typography sx={{ pr: 2 }} variant="subtitle1">
Grand Total:
</Typography>
<Typography variant="h6">{formatNumberToRupees(Math.round(selected.totalamount))}</Typography>
</Stack>
</Stack>
</Grid>
</Grid>
</Box>
</Box>
<Divider />
<Box sx={{ p: 2.5 }}>
<Typography>Notes: {selected.remarks}</Typography>
</Box>
<Divider />
</div>
</Box>
{/* ================================================= || updatePayment Dialog || ================================================= */}
<Dialog
open={paydialog}
onClose={() => {
setpaydialog(false);
}}
maxWidth={'sm'}
fullWidth
>
<DialogTitle sx={{ bgcolor: theme.palette.primary.main }}>
<Stack direction={'row'} spacing={1}>
<Typography variant="h2" sx={{ color: 'white' }}>
</Typography>
<Typography variant="h3" sx={{ color: 'white' }}>
Update Payment
</Typography>
</Stack>
</DialogTitle>
<DialogContent dividers>
<Stack spacing={1} sx={{ mb: 2 }}>
<Typography>Reference No</Typography>
<TextField
type="number"
placeholder="Enter Reference Number"
sx={{ width: '100%' }}
onChange={(e) => {
setRefnumber(e.target.value);
}}
/>
</Stack>
<Stack spacing={2} sx={{ mb: 2 }}>
<Typography>Remarks</Typography>
<TextField
multiline
required
placeholder="Enter Remarks"
sx={{ width: '100%' }}
onChange={(e) => {
setRemarks(e.target.value);
}}
/>
</Stack>
</DialogContent>
<DialogActions>
<Button
variant="outlined"
sx={{
'&:hover': {
backgroundColor: 'primary.main',
color: 'primary.contrastText'
},
m: 2
}}
onClick={() => {
setpaydialog(false);
}}
>
Cancel
</Button>
<Button
variant="outlined"
disabled={refnumber == '' || remarks == ''}
sx={{
'&:hover': {
backgroundColor: 'primary.main',
color: 'primary.contrastText'
},
m: 2
}}
onClick={() => {
setpaydialog(false);
updatePayment();
navigate('/nearle/invoice');
}}
>
Update
</Button>
</DialogActions>
</Dialog>
</>
);
};
export default InvoicePreview;

397
src/pages/nearle/login.js Normal file
View File

@@ -0,0 +1,397 @@
import { useState, useEffect } from 'react';
import { enqueueSnackbar, closeSnackbar } from 'notistack';
import AnimateButton from 'components/@extended/AnimateButton';
import OtpInput from 'react18-input-otp';
import { Box, Card, CardContent, Stack, TextField, Button, Typography, Link, FormLabel, IconButton, InputAdornment } from '@mui/material';
import { useTheme } from '@mui/material/styles';
import axios from 'axios';
import { useNavigate } from 'react-router-dom';
import Loader from 'components/Loader';
import logo from 'assets/images/logo-nearle1.png';
import { useSelector, useDispatch } from 'react-redux';
import { OpenToast } from 'components/third-party/OpenToast';
import { closeGlobalToast, GlobalToast } from 'components/nearle_components/GlobalToast';
import Visibility from '@mui/icons-material/Visibility';
import VisibilityOff from '@mui/icons-material/VisibilityOff';
import { setLoginUser } from 'store/reducers/loginUserSlice';
const Login = () => {
const dispatch = useDispatch();
const fcmtoken = useSelector((state) => state.fcm);
const permission = useSelector((state) => state.fcm.permission);
const theme = useTheme();
const [loading, setLoading] = useState(false);
let navigate = useNavigate();
const [otp, setOtp] = useState('');
const [currentotp, setCurrentotp] = useState('');
const [userinfo, setUserinfo] = useState({});
const [username, setUsername] = useState('');
const [passwordStatus, setPasswordStatus] = useState(0);
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [isPassword, setIspassword] = useState(false);
const [showPassword, setShowPassword] = useState(false);
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
const [userid, setUserid] = useState(0);
useEffect(() => {
if (localStorage.getItem('firstname')) {
navigate('/nearle/orders');
}
}, []);
const loginsend = async () => {
setLoading(true);
if (!username) {
opentoast('Fill All required fields');
setLoading(false);
return;
}
try {
const res = await axios.post(`https://jupiter.nearle.app/live/api/v1/users/console/login`, {
authname: username,
configid: 9, // 9 -> config id for nearle console admin
userfcmtoken: fcmtoken?.token,
password
});
// user not found
if (res.data.code == 409 && !res.data.status) {
OpenToast(res.data.message, 'error', 3000);
}
// user not activated
else if (res.data.code == 403) {
OpenToast(res.data.message, 'warning', 3000);
}
//user found, no password, setup password
else if (res.data.code == 409 && res.data.status) {
setPasswordStatus(1); // for password and confirm password ui
setUserid(res.data.details.userid);
OpenToast('User Found', 'success', 3000);
OpenToast(res.data.message, 'success', 3000);
}
//user found, incorrect password
else if (res.data.code == 401 && !res.data.status) {
OpenToast(res.data.message, 'error', 3000);
}
//user found, enter password
else if (res.data.code == 401 && res.data.status) {
OpenToast(res.data.message, 'success', 3000);
fetchAppLocations(res.data.userid);
setPasswordStatus(2);
}
// user found, correct password
else if (res.data.code == 200 && res.data.status) {
OpenToast(res.data.message, 'success', 1000);
setUserinfo(res.data.details);
const userinfo = res.data.details;
dispatch(setLoginUser(userinfo));
localStorage.setItem('firstname', userinfo.firstname);
localStorage.setItem('authname', userinfo.authname);
localStorage.setItem('roleid', userinfo.roleid);
localStorage.setItem('tenantid', userinfo.tenantid);
localStorage.setItem('partnerid', userinfo.partnerid);
localStorage.setItem('applocationid', userinfo.applocationid);
localStorage.setItem('userid', userinfo.userid);
localStorage.setItem('userfcmtoken', userinfo.userfcmtoken);
fetchAppLocations(userinfo.userid);
navigate('/nearle/orders');
} else {
OpenToast(res.data.message, 'error', 3000);
}
} catch (err) {
console.error(err);
OpenToast(err.message, 'error', 5000);
} finally {
setLoading(false);
}
};
const loginsuccessful = () => {
localStorage.setItem('firstname', userinfo.firstname);
localStorage.setItem('authname', userinfo.authname);
localStorage.setItem('roleid', userinfo.roleid);
localStorage.setItem('tenantid', userinfo.tenantid);
localStorage.setItem('partnerid', userinfo.partnerid);
localStorage.setItem('applocationid', userinfo.applocationid);
localStorage.setItem('userid', userinfo.userid);
localStorage.setItem('userfcmtoken', userinfo.userfcmtoken);
closeGlobalToast(); // to close the pin snackbar
navigate('/nearle/orders');
};
const opentoast = (message) => {
enqueueSnackbar(message, {
variant: 'error',
anchorOrigin: { vertical: 'top', horizontal: 'right' },
autoHideDuration: 1500
});
};
const fetchAppLocations = async (id) => {
const response = await axios.get(`${process.env.REACT_APP_URL}/partners/getlocations/?userid=${id}`);
const updatedLocations = [...response.data.details, { locationname: 'All', applocationid: 0 }];
localStorage.setItem('applocations', JSON.stringify(updatedLocations));
};
const updateUser = async () => {
const response = await axios.put(`${process.env.REACT_APP_URL2}/users/update`, {
userid,
password
});
if (response.data.status) {
OpenToast(response.data.message, 'success', 3000);
OpenToast('Enter Password to Login', 'success', 3000);
setPasswordStatus(2);
setPassword('');
}
};
return (
<Box sx={{ height: '100vh', position: 'relative' }}>
{loading && <Loader />}
<Stack
sx={{
minHeight: '100vh',
alignItems: 'center',
justifyContent: 'center',
px: 2
}}
>
<Card
sx={{
width: '100%',
maxWidth: 420,
borderRadius: 2,
border: `1px solid ${theme.palette.divider}`,
boxShadow: 'none',
p: { xs: 2, sm: 3 }
}}
>
{/* Logo */}
<Stack alignItems="center" mb={2}>
<img src={logo} alt="loginpagelogo" />
</Stack>
{/* Title */}
<Typography variant="h3" textAlign="start" mb={3}>
Login
</Typography>
<CardContent sx={{ p: 0 }}>
<form
noValidate
onSubmit={(e) => {
e.preventDefault();
if (passwordStatus == 0) {
loginsend();
} else if (passwordStatus == 1) {
if (!password || !confirmPassword || password != confirmPassword) {
OpenToast('Check Password', 'warning', 3000);
} else {
updateUser();
}
} else if (passwordStatus == 2) {
if (!password) {
OpenToast('Invalid Password', 'warning', 3000);
}
loginsend();
}
// if (currentotp) {
// if (currentotp == otp) {
// loginsuccessful();
// fetchAppLocations();
// } else {
// opentoast('Invalid pin');
// }
// }
}}
>
<Stack spacing={3}>
{/* Email */}
<TextField
autoFocus
fullWidth
label="E-mail Address"
variant="outlined"
autoComplete="email"
required
value={username}
onChange={(e) => setUsername(e.target.value.toLocaleLowerCase())}
InputProps={{ readOnly: passwordStatus }}
/>
{/* Setup Password */}
{passwordStatus == 1 && (
<Stack display={'flex'} flexDirection={'column'} spacing={3}>
<Typography variant="h4" textAlign="start" mb={3}>
Setup Password
</Typography>
<TextField
autoFocus
fullWidth
label="Enter New Password"
variant="outlined"
required
type={showPassword ? 'text' : 'password'}
value={password}
onChange={(e) => setPassword(e.target.value)}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={() => setShowPassword((prev) => !prev)} edge="end">
{showPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
</InputAdornment>
)
}}
/>
<TextField
error={confirmPassword !== '' && password !== confirmPassword}
fullWidth
label="Re-Enter Password"
variant="outlined"
required
type={showConfirmPassword ? 'text' : 'password'}
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={() => setShowConfirmPassword((prev) => !prev)} edge="end">
{showConfirmPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
</InputAdornment>
)
}}
/>
</Stack>
)}
{/* Enter Password */}
{passwordStatus == 2 && (
<Stack display={'flex'} flexDirection={'column'} spacing={3}>
<Typography variant="h4" textAlign="start" mb={3}>
Enter Password
</Typography>
<TextField
autoFocus
fullWidth
label="Enter Password"
variant="outlined"
required
type={showPassword ? 'text' : 'password'}
value={password}
onChange={(e) => setPassword(e.target.value)}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={() => setShowPassword((prev) => !prev)} edge="end">
{showPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
</InputAdornment>
)
}}
/>
</Stack>
)}
{/* OTP */}
{isPassword && (
<Stack spacing={1.5}>
<Stack direction="row" justifyContent="space-between">
<FormLabel>Enter Password</FormLabel>
<Link
variant="body2"
sx={{ cursor: 'pointer' }}
onClick={() => {
setOtp('');
loginsend();
}}
>
Retry
</Link>
</Stack>
{/* <OtpInput
shouldAutoFocus
value={otp}
onChange={(otp) => setOtp(otp)}
numInputs={4}
containerStyle={{ justifyContent: 'space-between' }}
inputStyle={{
width: 48,
height: 48,
borderRadius: 8,
border: `1px solid ${borderColor}`,
fontSize: 18
}}
focusStyle={{
outline: 'none',
border: `1px solid ${theme.palette.primary.main}`,
boxShadow: theme.customShadows.primary
}}
/> */}
<TextField type="passowrd" value={password} onChange={(e) => setPassword(e.target.value)} />
</Stack>
)}
{/* Submit */}
<AnimateButton>
<Button fullWidth size="large" type="submit" variant="contained" color="primary">
Continue
</Button>
</AnimateButton>
</Stack>
</form>
</CardContent>
</Card>
</Stack>
{/* footer */}
<Stack
sx={{
position: 'absolute',
bottom: 10,
width: '100%',
display: 'flex',
flexDirection: 'row',
justifyContent: 'end',
gap: 2,
p: 2
}}
>
<Typography
variant="subtitle1"
color="secondary"
component={Link}
href="https://nearle.in"
target="_blank"
sx={{ display: 'flex' }}
>
&copy; All rights reserved
</Typography>
<Typography
variant="subtitle1"
color="secondary"
component={Link}
href="https://nearle.in/terms"
target="_blank"
sx={{ display: 'flex' }}
>
Terms and Conditions
</Typography>
<Typography
variant="subtitle1"
color="secondary"
component={Link}
href="https://nearle.in/privacy"
target="_blank"
sx={{ display: 'flex' }}
>
Privacy Policy
</Typography>
</Stack>
</Box>
);
};
export default Login;

513
src/pages/nearle/login1.js Normal file
View File

@@ -0,0 +1,513 @@
import { useState, useEffect } from 'react';
import { useSelector } from 'react-redux';
// import AuthWrapper from 'sections/auth/AuthWrapper';
import {
Box,
Grid,
Card,
CardContent,
// CardHeader,
Stack,
// Divider,
// InputLabel,
// OutlinedInput,
TextField,
Button,
Typography,
CardHeader,
Container,
Link
} from '@mui/material';
import { useTheme } from '@mui/material/styles';
import AnimateButton from 'components/@extended/AnimateButton';
import logo from 'assets/images/logo-nearle1.png';
import axios from 'axios';
import { useNavigate } from 'react-router-dom';
// import { openSnackbar } from 'store/reducers/snackbar';
// import { useDispatch } from 'react-redux';
import Loader from 'components/Loader';
import { enqueueSnackbar } from 'notistack';
const Login = () => {
const theme = useTheme();
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [alertmessage, setAlertmessage] = useState('');
const [checkusername, setCheckusername] = useState(false);
// const [toast, setToast] = useState(false);
const [loading, setLoading] = useState(false);
let navigate = useNavigate();
// let dispatch = useDispatch();
const [submitting, setSubmitting] = useState(false);
// let loginuserid = useSelector((state)=>state.logininfo);
// useEffect(() => {
// if (alertmessage) {
// dispatch(
// openSnackbar({
// open: true,
// message: alertmessage,
// variant: 'alert',
// anchorOrigin: { vertical: 'top', horizontal: 'right' },
// alert: {
// // variant:'info',
// color: 'error',
// }
// })
// )
// }
// }, [toast])
useEffect(() => {
if (
localStorage.getItem('authname')
// || localStorage.getItem("appuserid")
) {
navigate('/deliveries');
}
// console.log(alertmessage)
}, []);
const usernamecheck = async (e) => {
e.preventDefault();
setUsername(e.target.value);
if (e.target.value) {
try {
// await axios.post(`${process.env.REACT_APP_URL}/auth/login`, {
// "authname": e.target.value
// })
await axios
.post(`${process.env.REACT_APP_URL}/users/login`, {
authname: e.target.value,
configid: 1
// "contactno": e.target.value,
// "password": 'admin'
})
.then((res) => {
console.log(res.data);
if (res.data.details.authname === e.target.value) {
setUsername(e.target.value);
setCheckusername(false);
} else {
setCheckusername(true);
}
// if (res.data.authname === e.target.value) {
// setUsername(e.target.value);
// setCheckusername(false);
// }
})
.catch((err) => {
// if (err.response.data.message === 'No user found') {
setCheckusername(true);
// }
});
} catch (err) {
console.log(err);
}
}
};
const loginsend = async () => {
// e.preventDefault();
setLoading(true);
if (password && username) {
if (password == 'admin') {
setSubmitting(true);
try {
await axios
.post(`${process.env.REACT_APP_URL}/users/partner/login`, {
// "authname": username,
configid: 1,
contactno: username
// "password": password
})
.then((res) => {
console.log(res.data);
if (res.data.status) {
if (res.data.details.contactno === username) {
enqueueSnackbar('login Successfull', {
variant: 'success',
anchorOrigin: { vertical: 'top', horizontal: 'right' },
autoHideDuration: 3000
});
setUsername('');
setPassword('');
localStorage.setItem('firstname', res.data.details.tenantname);
localStorage.setItem('authname', res.data.details.authname);
// localStorage.setItem("appuserid", res.data.details.userid);
localStorage.setItem('roleid', res.data.details.roleid);
localStorage.setItem('tenantid', res.data.details.tenantid);
localStorage.setItem('partnerid', res.data.details.partnerid);
navigate('/orders');
setSubmitting(false);
}
}
console.log(res.data.message);
setLoading(false);
})
.catch((err) => {
console.log(err);
// setAlertmessage('Invalid Data');
// if(err.message == 'Network Error'){
opentoast(err.message);
// }else{
// opentoast('Invalid Data');
// }
setLoading(false);
setSubmitting(false);
console.log(err.message);
});
} catch (err) {
console.log(err);
setLoading(false);
setSubmitting(false);
}
} else {
opentoast('Password is Incorrect');
setLoading(false);
}
} else {
// let el2 = document.getElementById('toastid');
// el2.classList.add('d-block');
// el2.classList.remove('d-none');
setAlertmessage('Fill All required fields');
opentoast('Fill All required fields');
setLoading(false);
}
};
// const handleClose = () => {
// setToast(false)
// }
const opentoast = (message) => {
// setToast(true)
// setTimeout(() => {
// // handleClose();
// setToast(false)
// }, 2000);
enqueueSnackbar(message, {
variant: 'error',
anchorOrigin: { vertical: 'top', horizontal: 'right' },
autoHideDuration: 2000
});
};
return (
<>
{/* <AuthWrapper> */}
<Box sx={{ minHeight: '100vh' }}>
{loading && <Loader />}
{/* <AuthBackground /> */}
<Grid
container
direction="column"
justifyContent="flex-start"
sx={{
minHeight: '100vh'
}}
>
<Grid
item
xs={12}
// sx={{ ml: 3, mt: 3 }}
sx={{ ml: 3, mt: 1 }}
>
<img src={logo} alt="legendary" width="200px" />
</Grid>
<Grid item xs={12}>
<Grid
item
xs={12}
container
justifyContent="center"
alignItems="center"
// sx={{ minHeight: { xs: 'calc(100vh - 210px)', sm: 'calc(100vh - 134px)', md: 'calc(100vh - 112px)' } }}
sx={{ minHeight: { xs: 'calc(100vh - 210px)', sm: 'calc(100vh - 134px)', md: 'calc(100vh - 140px)' } }}
>
<Grid item>
{/* <AuthCard>{children}</AuthCard> */}
<Box
sx={{
maxWidth: { xs: 400, lg: 475 },
margin: { xs: 2.5, md: 3 },
'& > *': {
flexGrow: 1,
flexBasis: '50%'
}
}}
>
<Card
sx={{
position: 'relative',
border: '1px solid',
borderRadius: 1,
borderColor: theme.palette.divider,
boxShadow: 'inherit',
p: 2,
width: '100%'
}}
>
{/* <CardHeader title={<Typography variant="h4">Login</Typography>} /> */}
{/* <Divider sx={{ borderStyle: 'dashed' }} /> */}
{/* <h1>eee</h1> */}
{/* <CardHeader> */}
<Grid container spacing={3}>
<Grid item xs={12}>
<Stack direction="row" justifyContent="flex-start" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
<CardHeader title={<Typography variant="h3">Login</Typography>} />
</Stack>
</Grid>
{/* <Grid item xs={12}>
<AuthLogin isDemo={isLoggedIn} />
</Grid> */}
</Grid>
<CardContent>
<form
noValidate
onSubmit={(e) => {
e.preventDefault();
}}
>
<Grid container spacing={2}>
<Grid item xs={12}>
{/* <Stack spacing={1}> */}
{/* <InputLabel htmlFor="email-login">Email Address</InputLabel>
<OutlinedInput
id="email-login"
type="email"
value={values.email}
name="email"
onBlur={handleBlur}
onChange={handleChange}
placeholder="Enter email address"
fullWidth
id="username1"
label="E-mail Address"
variant="outlined"
autoComplete='email'
required
onChange={usernamecheck}
error={checkusername}
error={Boolean(touched.email && errors.email)}
/>
{touched.email && errors.email && (
<FormHelperText error id="standard-weight-helper-text-email-login">
{errors.email}
</FormHelperText>
)} */}
<TextField
margin="normal"
fullWidth
id="username1"
label="E-mail Address"
variant="outlined"
autoComplete="email"
required
onChange={usernamecheck}
error={checkusername}
/>
<TextField
margin="normal"
fullWidth
required
autoComplete="current-password"
onChange={(e) => setPassword(e.target.value)}
type="password"
id="password1"
label="Password"
variant="outlined"
/>
{/* </Stack> */}
</Grid>
<Grid item xs={12}>
{/* <Stack spacing={0}> */}
{/* <InputLabel htmlFor="password-login">Password</InputLabel> */}
{/* <OutlinedInput
fullWidth
// error={Boolean(touched.password && errors.password)}
// id="-password-login"
// type={showPassword ? 'text' : 'password'}
// value={values.password}
// name="password"
// onBlur={handleBlur}
// onChange={handleChange}
// endAdornment={
// <InputAdornment position="end">
// <IconButton
// aria-label="toggle password visibility"
// onClick={handleClickShowPassword}
// onMouseDown={handleMouseDownPassword}
// edge="end"
// color="secondary"
// >
// {showPassword ? <EyeOutlined /> : <EyeInvisibleOutlined />}
// </IconButton>
// </InputAdornment>
// }
placeholder="Enter password"
// margin="normal"
// fullWidth
required
autoComplete="current-password"
onChange={(e) => setPassword(e.target.value)}
type='password' id="password1"
/> */}
{/* {touched.password && errors.password && (
<FormHelperText error id="standard-weight-helper-text-password-login">
{errors.password}
</FormHelperText>
)} */}
{/* </Stack> */}
<Link href="#" variant="h6">
Forgot password?
</Link>
</Grid>
{/* <Grid item xs={12} sx={{ mt: -1 }}>
<Stack direction="row" justifyContent="space-between" alignItems="center" spacing={2}>
<FormControlLabel
control={
<Checkbox
checked={checked}
onChange={(event) => setChecked(event.target.checked)}
name="checked"
color="primary"
size="small"
/>
}
label={<Typography variant="h6">Keep me sign in</Typography>}
/>
<Link variant="h6" component={RouterLink} to={isDemo ? '/auth/forgot-password' : '/forgot-password'} color="text.primary">
Forgot Password?
</Link>
</Stack>
</Grid> */}
{/* {errors.submit && (
<Grid item xs={12}>
<FormHelperText error>{errors.submit}</FormHelperText>
</Grid>
)} */}
<Grid item xs={12}>
{/* <AnimateButton> */}
<AnimateButton>
<Button
disabled={submitting}
onClick={() => {
loginsend();
// navigate('/dashboard')
}}
fullWidth
size="large"
type="submit"
variant="contained"
color="primary"
>
Login
</Button>
</AnimateButton>
{/* </AnimateButton> */}
</Grid>
</Grid>
{/* </Grid> */}
</form>
</CardContent>
{/* </CardHeader> */}
</Card>
</Box>
</Grid>
</Grid>
</Grid>
<Grid
item
xs={12}
// sx={{ m: 3, mt: 1 }}
sx={{ mb: 1 }}
>
{/* <AuthFooter /> */}
<Container maxWidth="xl">
<Stack
direction={{ sx: 'column', md: 'row' }}
justifyContent={{ sx: 'center', md: 'space-between' }}
spacing={2}
// textAlign={{ sx: 'center', md: 'inherit' }}
alignItems={{ sx: 'center', md: 'inherit' }}
width="100%"
>
<Stack direction="row" justifyContent="center" spacing={1}>
<Typography variant="subtitle2" color="secondary" component="span" sx={{ display: 'flex' }}>
&copy; All rights reserved
{/* <Typography variant="subtitle2" href="#mantis-privacy" target="_blank" underline="hover" sx={{ml:1}}>Privacy Policy</Typography> */}
</Typography>
</Stack>
<Stack
direction={{ sx: 'column', md: 'row' }}
spacing={{ sx: 1, md: 3 }}
textAlign={{ sx: 'center', md: 'inherit' }}
alignItems={{ sx: 'center', md: 'inherit' }}
// width='100%'
>
<Typography
variant="subtitle2"
color="secondary"
component={Link}
href="#"
// target="_blank"
underline="hover"
textAlign="center"
>
Terms and Conditions
</Typography>
<Typography
variant="subtitle2"
color="secondary"
component={Link}
href="#"
// target="_blank"
underline="hover"
textAlign="center"
>
Privacy Policy
</Typography>
{/* <Typography
variant="subtitle2"
color="secondary"
component={Link}
href="#"
// target="_blank"
underline="hover"
textAlign='center'
>
CA Privacy Notice
</Typography> */}
</Stack>
</Stack>
</Container>
</Grid>
</Grid>
</Box>
{/* </AuthWrapper> */}
</>
);
};
export default Login1;

View File

@@ -0,0 +1,623 @@
import {
Autocomplete,
Button,
Chip,
Divider,
Grid,
Stack,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
TextField,
Tooltip,
Typography,
Backdrop,
IconButton
} from '@mui/material';
import React, { Fragment, useEffect, useMemo, useState } from 'react';
import { useTheme } from '@mui/material/styles';
import { useLocation, useNavigate } from 'react-router-dom';
import dayjs from 'dayjs';
import MainCard from 'components/MainCard';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import { fetchPaymentType, fetchRidersList, finalCreatedeliveries, notifyRider } from 'pages/api/api';
import { OpenToast } from 'components/third-party/OpenToast';
import { useMutation, useQuery } from '@tanstack/react-query';
import Loader from 'components/Loader';
import CircularLoader from 'components/CircularLoader';
import { Empty } from 'antd';
import HoverSocialCard from 'components/cards/statistics/HoverSocialCard';
import { DashboardFilled, OpenAIFilled } from '@ant-design/icons';
import { MdDirectionsBike } from 'react-icons/md';
import { FaMapLocationDot } from 'react-icons/fa6';
import { HiOutlineArrowLeft } from 'react-icons/hi';
var utc = require('dayjs/plugin/utc');
dayjs.extend(utc);
const OrdersPreview = () => {
const theme = useTheme();
const navigate = useNavigate();
const location = useLocation();
console.log('location.state', location.state);
const [rider, setRider] = useState(null);
const [payment, setPayment] = useState(null);
const [finaldeliveryList, setFinalDeliveryList] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const deliverylist = location.state?.deliverylist;
const zoneData = location.state?.zoneData;
const metaData = location.state?.metaData;
const riderToken = location.state?.riderToken;
const appId = location.state?.appId;
const aiMode = location.state?.aiMode;
const reassignOrders = location.state?.reassignOrders;
useEffect(() => {
console.log('aiMode', aiMode);
console.log('riderToken', riderToken);
console.log('zoneData', zoneData);
console.log('metaData', metaData);
console.log('reassignOrders', reassignOrders);
}, []);
useEffect(() => {
if (!deliverylist?.length) return;
const updateDeliveryAmtList = deliverylist.map((list) => {
const cumulativeKms = Number(list.cumulativekms || 0);
const minKm = Number(list.minkm || 0);
const basePrice = Number(list.baseprice || 0);
const pricePerKm = Number(list.priceperkm || 0);
if (cumulativeKms <= minKm) {
return {
...list,
deliveryamt: basePrice
};
}
return {
...list,
deliveryamt: (cumulativeKms - minKm) * pricePerKm + basePrice
};
});
setFinalDeliveryList(updateDeliveryAmtList);
console.log('finaldeliveryList', updateDeliveryAmtList);
}, [deliverylist]);
// ==============================|| fetchPaymentType ||============================== //
const {
data: paymentModes = [],
isLoading: paymentModesLoading,
isError: paymentModesError,
error: paymentModesErrorMessage
} = useQuery({
queryKey: ['paymentmodes'],
queryFn: fetchPaymentType
});
// ==============================|| fetchRidersList ||============================== //
const {
data: ridersList = [],
isLoading: ridersListLoading,
isError: ridersListError,
error: ridersListErrorMessage,
refetch: ridersListRefetch
} = useQuery({
queryKey: ['ridersList', appId], // Unique key for caching & re-fetching
queryFn: fetchRidersList,
enabled: appId !== 0 // Ensures query runs only when appId is valid
});
const getRiderName = async (userid) => {
await ridersList.map((rider) => {
if (rider.userid == userid) {
return rider.firstname;
}
});
};
// ======================================================= || notifyRiderMutation || =======================================================
const notifyRiderMutation = useMutation({
mutationFn: notifyRider, // Using the separate function
onSuccess: () => {
OpenToast('Notification sent Successfully', 'success', 2000);
},
onError: (error) => {
OpenToast(error.message, 'error', 2000);
}
});
const createNormalDeliveryMutation = useMutation({
mutationFn: finalCreatedeliveries, // for optimised delivery create
onSuccess: (data, variables) => {
console.log('data', data);
console.log('varialbles', variables);
notifyRiderMutation.mutate(rider.userfcmtoken || riderToken); // Call notifyRider after success
if (data.status == 'accepted') {
OpenToast('Delivery Created Successfully', 'success', 2000);
}
setTimeout(() => {
setIsLoading(false);
navigate('/nearle/deliveries');
}, 2000);
},
onError: (error) => {
OpenToast(error.message, 'error', 4000);
}
});
const handleManualCreateDelivery = async () => {
setIsLoading(true);
createNormalDeliveryMutation.mutate({
deliveries: finaldeliveryList
});
};
return (
<MainCard
content={false}
title={
<Stack direction="row" alignItems="center" spacing={1} sx={{ ml: 1 }}>
<Tooltip title="Back to orders" placement="top">
<IconButton
onClick={() => navigate('/nearle/orders')}
sx={{
backgroundColor: 'action.hover',
color: 'text.primary',
'&:hover': {
backgroundColor: 'action.selected'
}
}}
>
<HiOutlineArrowLeft size={22} />
</IconButton>{' '}
</Tooltip>
<Typography sx={{ m: 2 }} variant="h3">
Assign Orders
</Typography>
</Stack>
}
secondary={
<Button sx={{ m: 2 }} color="primary" variant="contained" startIcon={<ArrowBackIcon />}>
Re-Assign
</Button>
}
>
{(paymentModesLoading || ridersListLoading || isLoading) && (
<>
<Loader />
<CircularLoader />
</>
)}
{
<Backdrop
sx={{
color: '#fff',
zIndex: (theme) => theme.zIndex.drawer + 1
}}
open={paymentModesLoading || ridersListLoading || isLoading} // when loader = true, backdrop covers the page
>
<CircularLoader color="inherit" />
</Backdrop>
}
{aiMode == 1 && (
<Stack sx={{ m: 2 }}>
<Grid container spacing={2}>
<Grid item xs={12} sm={3}>
<HoverSocialCard
secondary={metaData?.total_orders}
primary={'Orders'}
percentage={<DashboardFilled />}
color={theme.palette.success.main}
sx={{ cursor: 'pointer' }}
/>
</Grid>
<Grid item xs={12} sm={3}>
<HoverSocialCard
secondary={metaData?.total_riders}
primary={'Riders'}
percentage={<MdDirectionsBike />}
color={theme.palette.warning.main}
/>
</Grid>
<Grid item xs={12} sm={3}>
<HoverSocialCard
secondary={zoneData?.length}
primary={'Zones'}
percentage={<FaMapLocationDot />}
color={theme.palette.info.main}
/>
</Grid>
<Grid item xs={12} sm={3}>
<HoverSocialCard
secondary={zoneData?.length}
primary={'Kilometer'}
percentage={<FaMapLocationDot />}
color={theme.palette.error.main}
/>
</Grid>
</Grid>
</Stack>
)}
<TableContainer
sx={{
maxHeight: 'calc(100vh - 250px)',
overflow: 'auto',
'&::-webkit-scrollbar': {
width: '12px', // scroll bar width
cursor: 'pointer'
},
'&::-webkit-scrollbar-thumb': {
backgroundColor: theme.palette.primary.main, // thumb color
borderRadius: '8px',
cursor: 'pointer'
},
'&::-webkit-scrollbar-thumb:hover': {
backgroundColor: theme.palette.primary.dark, // hover color
cursor: 'pointer'
},
'&::-webkit-scrollbar-track': {
backgroundColor: theme.palette.primary.lighter,
cursor: 'pointer'
}
}}
>
<Table stickyHeader>
<TableHead>
<TableRow sx={{ backgroundColor: 'red' }}>
<TableCell sx={{ position: 'sticky !important', backgroundColor: theme.palette.secondary.light }}>#</TableCell>
{aiMode == 1 && (
<TableCell sx={{ position: 'sticky !important', backgroundColor: theme.palette.secondary.light }}>Zone </TableCell>
)}
<TableCell sx={{ position: 'sticky !important', backgroundColor: theme.palette.secondary.light }}>Tenant </TableCell>
<TableCell sx={{ position: 'sticky !important', backgroundColor: theme.palette.secondary.light }}>order Location</TableCell>
<TableCell sx={{ position: 'sticky !important', backgroundColor: theme.palette.secondary.light }}>Pickup </TableCell>
<TableCell sx={{ position: 'sticky !important', backgroundColor: theme.palette.secondary.light }}>Delivery</TableCell>
<TableCell sx={{ position: 'sticky !important', backgroundColor: theme.palette.secondary.light }}>Notes</TableCell>
{aiMode == 1 && (
<TableCell sx={{ position: 'sticky !important', backgroundColor: theme.palette.secondary.light }}>Rider</TableCell>
)}
<TableCell align="center" sx={{ position: 'sticky !important', backgroundColor: theme.palette.secondary.light }}>
Type
</TableCell>
<TableCell align="center" sx={{ position: 'sticky !important', backgroundColor: theme.palette.secondary.light }}>
Profit
</TableCell>
<TableCell align="center" sx={{ position: 'sticky !important', backgroundColor: theme.palette.secondary.light }}>
Charges
</TableCell>
<TableCell align="center" sx={{ position: 'sticky !important', backgroundColor: theme.palette.secondary.light }}>
KMS
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{finaldeliveryList?.length == 0 && (
<TableRow>
<TableCell colSpan={10}>
<Empty />
</TableCell>
</TableRow>
)}
{finaldeliveryList && aiMode == 1 // ai mode , ai automation
? finaldeliveryList?.map((val, index) => {
return (
<Fragment key={index}>
<TableRow sx={{}}>
<TableCell>
<Typography> {index + 1}</Typography>
</TableCell>
{aiMode == 1 && (
<TableCell>
<Chip color="primary" label={val.zone_name} />
</TableCell>
)}
<TableCell>
<Tooltip title={val.tenantaddress}>
<Typography variant="body1" noWrap>
{val.tenantname}
</Typography>
<Typography noWrap sx={{ fontSize: '11px' }}>
{val.tenantsuburb}
<br />
</Typography>
<Typography noWrap variant="body2">
{val.applocation}
</Typography>
</Tooltip>
</TableCell>
<TableCell align="left">
<Tooltip title={val.locationaddress} placement="top">
<Typography variant="body1" noWrap>
{`${val.locationname}-(${val.locationsuburb})`}
</Typography>
</Tooltip>
<Tooltip title="Order Id">
<Typography variant="body2" noWrap>
{val.orderid}
</Typography>
</Tooltip>
<Stack display={'flex'} flexDirection={'row'} gap={3}>
<Tooltip title="Ordered date">
<Typography noWrap sx={{ fontSize: '12px' }}>
{dayjs(val.orderdate).utc().format('DD/MM/YYYY')}
</Typography>
<Typography noWrap sx={{ fontSize: '11px' }}>
{dayjs(val.orderdate).utc().format('hh:mm A')}
</Typography>
</Tooltip>
-
<Tooltip title="Delivery date">
<Typography noWrap sx={{ fontSize: '12px' }}>
{dayjs(val.deliverydate).utc().format('DD/MM/YYYY')}
</Typography>
<Typography noWrap sx={{ fontSize: '11px' }}>
{dayjs(val.deliverydate).utc().format('hh:mm A')}
</Typography>
</Tooltip>
</Stack>
</TableCell>
<TableCell align="left">
<Stack direction={'row'} spacing={1}>
<Stack direction="column">
<Typography variant="caption">{val.pickupcustomer}</Typography>
<Typography variant="caption">{val.pickupcontactno}</Typography>
<Tooltip title={val.pickupaddress}>
<Typography variant="caption">{val.pickupsuburb || val.pickupaddress.slice(0, 20)}</Typography>
</Tooltip>
</Stack>
</Stack>
</TableCell>
<TableCell align="left">
<Stack direction={'row'} spacing={1}>
<Stack direction="column">
<Typography variant="caption">{val.deliverycustomer}</Typography>
<Typography variant="caption">{val.deliverycontactno}</Typography>
<Tooltip title={val.deliveryaddress}>
<Typography variant="caption">{val.deliverysuburb || val.deliveryaddress.slice(0, 20)}</Typography>
</Tooltip>
</Stack>
</Stack>
</TableCell>
<TableCell align="left">{val.ordernotes}</TableCell>
{aiMode == 1 && (
<TableCell align="left">
<Typography sx={{ whiteSpace: 'nowrap' }}>{val.username}</Typography>
<Typography>ID : {val.userid}</Typography>
</TableCell>
)}
<TableCell align="center">
<Chip
size="small"
label={val.ordertype}
color={val.ordertype == 'Economy' ? 'success' : val.ordertype == 'Risky' ? 'error' : 'primary'}
/>
</TableCell>
<TableCell align="center">
<Stack display={'flex'} flexDirection={'column'} gap={1} sx={{ cursor: 'pointer' }}>
<Tooltip title="Charges" placement="top">
<Chip size="small" label={`${val.deliverycharge.toFixed(2)} `} color="error" />
</Tooltip>
<Tooltip title="Amount" placement="left">
<Chip size="small" label={`${val.deliveryamt.toFixed(2)} `} color="success" />
</Tooltip>
</Stack>
</TableCell>
<TableCell align="center">
<Stack display={'flex'} flexDirection={'column'} gap={1} sx={{ cursor: 'pointer' }}>
<Tooltip title="KMS" placement="top">
<Chip size="small" label={`${val.kms} km`} color="error" />
</Tooltip>
<Tooltip title="Cumulative Kms" placement="right">
<Chip size="small" label={`${val.cumulativekms} km`} color="success" />
</Tooltip>
</Stack>
</TableCell>
</TableRow>
</Fragment>
);
})
: // normal optimisation
finaldeliveryList?.map((val, index) => {
return (
<Fragment key={index}>
<TableRow sx={{}}>
<TableCell>
<Typography> {index + 1}</Typography>
</TableCell>
{/* {aiMode == 1 && (
<TableCell>
<Chip color="primary" label={val.zone_name} />
</TableCell>
)} */}
<TableCell>
<Tooltip title={val.tenantaddress}>
<Typography variant="body1" noWrap>
{val.tenantname}
</Typography>
<Typography noWrap sx={{ fontSize: '11px' }}>
{val.tenantsuburb}
<br />
</Typography>
<Typography noWrap variant="body2">
{val.applocation}
</Typography>
</Tooltip>
</TableCell>
<TableCell align="left">
<Tooltip title={val.locationaddress} placement="top">
<Typography variant="body1" noWrap>
{`${val.locationname}-(${val.locationsuburb})`}
</Typography>
</Tooltip>
<Tooltip title="Order Id">
<Typography variant="body2" noWrap>
{val.orderid}
</Typography>
</Tooltip>
<Stack display={'flex'} flexDirection={'row'} gap={3}>
<Tooltip title="Ordered date">
<Typography noWrap sx={{ fontSize: '12px' }}>
{dayjs(val.orderdate).utc().format('DD/MM/YYYY')}
</Typography>
<Typography noWrap sx={{ fontSize: '11px' }}>
{dayjs(val.orderdate).utc().format('hh:mm A')}
</Typography>
</Tooltip>
-
<Tooltip title="Delivery date">
<Typography noWrap sx={{ fontSize: '12px' }}>
{dayjs(val.deliverydate).utc().format('DD/MM/YYYY')}
</Typography>
<Typography noWrap sx={{ fontSize: '11px' }}>
{dayjs(val.deliverydate).utc().format('hh:mm A')}
</Typography>
</Tooltip>
</Stack>
</TableCell>
<TableCell align="left">
<Stack direction={'row'} spacing={1}>
<Stack direction="column">
<Typography variant="caption">{val.pickupcustomer}</Typography>
<Typography variant="caption">{val.pickupcontactno}</Typography>
<Tooltip title={val.pickupaddress}>
<Typography variant="caption">{val.pickupsuburb || val.pickupaddress.slice(0, 20)}</Typography>
</Tooltip>
</Stack>
</Stack>
</TableCell>
<TableCell align="left">
<Stack direction={'row'} spacing={1}>
<Stack direction="column">
<Typography variant="caption">{val.deliverycustomer}</Typography>
<Typography variant="caption">{val.deliverycontactno}</Typography>
<Tooltip title={val.deliveryaddress}>
<Typography variant="caption">{val.deliverysuburb || val.deliveryaddress.slice(0, 20)}</Typography>
</Tooltip>
</Stack>
</Stack>
</TableCell>
<TableCell align="left">{val.ordernotes}</TableCell>
{/* {aiMode == 1 && (
<TableCell align="left">
<Typography sx={{ whiteSpace: 'nowrap' }}>{val.username}</Typography>
<Typography>ID : {val.userid}</Typography>
</TableCell>
)} */}
<TableCell align="center">
<Chip
size="small"
label={val.ordertype}
color={val.ordertype == 'Economy' ? 'success' : val.ordertype == 'Risky' ? 'error' : 'primary'}
/>
</TableCell>
<TableCell align="center">
<Stack display={'flex'} flexDirection={'column'} gap={1} sx={{ cursor: 'pointer' }}>
<Tooltip title="Charges" placement="top">
<Chip size="small" label={`${val.deliverycharge.toFixed(2)} `} color="error" />
</Tooltip>
<Tooltip title="Amount" placement="left">
<Chip size="small" label={`${val.deliveryamt.toFixed(2)} `} color="success" />
</Tooltip>
</Stack>
</TableCell>
<TableCell align="center">
<Stack display={'flex'} flexDirection={'column'} gap={1} sx={{ cursor: 'pointer' }}>
<Tooltip title="KMS" placement="top">
<Chip size="small" label={`${val.kms} km`} color="error" />
</Tooltip>
<Tooltip title="Cumulative Kms" placement="right">
<Chip size="small" label={`${val.cumulativekms} km`} color="success" />
</Tooltip>
</Stack>
</TableCell>
</TableRow>
</Fragment>
);
})}
</TableBody>
</Table>
</TableContainer>
<Divider />
{aiMode == 0 && (
<Grid container spacing={2} sx={{ p: 2 }}>
<Grid item xs={12} sm={6}>
<Autocomplete
id="free-solo-demo"
options={paymentModes}
renderInput={(params) => <TextField {...params} label="Choose Payment" />}
onChange={(event, newValue, reason) => {
if (reason === 'clear') {
setPayment(null);
return;
}
if (newValue) {
console.log('Selected:', newValue);
setPayment(newValue);
const newList = finaldeliveryList?.map((list) => ({
...list,
paymenttype: newValue.apptypeid // merge selected rider into each list item
}));
setFinalDeliveryList(newList);
}
}}
/>
</Grid>
<Grid item xs={12} sm={6}>
<Autocomplete
id="free-solo-demo"
options={ridersList}
renderInput={(params) => <TextField {...params} label="Choose Rider" />}
onChange={(event, newValue, reason) => {
if (reason === 'clear') {
setRider(null);
return;
}
if (newValue) {
setRider(newValue);
console.log('Selected:', newValue);
const newList = finaldeliveryList?.map((list) => ({
...list,
userid: newValue.userid,
userfcmtoken: newValue.userfcmtoken
}));
setFinalDeliveryList(newList);
}
}}
/>
</Grid>
</Grid>
)}
<Divider />
<Stack display={'flex'} flexDirection={'row'} gap={2} alignItems={'center'} justifyContent={'end'} sx={{ p: 2 }}>
<Button
sx={{}}
variant="contained"
color="secondary"
startIcon={<ArrowBackIcon />}
onClick={() => {
navigate('/nearle/orders');
}}
>
Back
</Button>
<Button sx={{ my: 2 }} variant="contained" disabled={aiMode === 0 && (!rider || !payment)} onClick={handleManualCreateDelivery}>
Assign Orders
</Button>
</Stack>
</MainCard>
);
};
export default OrdersPreview;

View File

@@ -0,0 +1,49 @@
import { TableRow, TableCell, Skeleton, Stack } from '@mui/material';
export const OrdersTableSkeleton = ({ rowsPerPage = 5, col = 1 }) => {
return (
<>
{Array.from(new Array(rowsPerPage)).map((_, index) => (
<TableRow key={index}>
{/* Checkbox */}
<TableCell>
<Skeleton variant="circular" width={24} height={24} />
</TableCell>
{/* Serial Number */}
<TableCell>
<Skeleton variant="text" width={30} />
</TableCell>
{/* Delivery Info */}
{Array.from({ length: col }).map((_, index) => (
<TableCell key={index}>
<Stack spacing={0.5}>
<Skeleton variant="text" width={100} />
<Skeleton variant="text" width={80} />
</Stack>
</TableCell>
))}
{/* Notes */}
<TableCell>
<Skeleton variant="text" width={150} />
</TableCell>
{/* Order Status */}
<TableCell>
<Skeleton variant="rounded" width={60} height={24} />
</TableCell>
{/* Actions */}
<TableCell align="center">
<Stack direction="row" spacing={1} justifyContent="flex-end">
<Skeleton variant="circular" width={28} height={28} />
<Skeleton variant="circular" width={28} height={28} />
</Stack>
</TableCell>
</TableRow>
))}
</>
);
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,610 @@
import {
Autocomplete,
Button,
Chip,
Divider,
Grid,
Stack,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
TextField,
Tooltip,
Typography,
Backdrop,
IconButton,
Badge,
Accordion,
AccordionSummary,
AccordionActions,
AccordionDetails
} from '@mui/material';
import React, { Fragment, useEffect, useMemo, useState } from 'react';
import { useTheme } from '@mui/material/styles';
import { useLocation, useNavigate } from 'react-router-dom';
import dayjs from 'dayjs';
import MainCard from 'components/MainCard';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import { createAutomationDeliveries, fetchPaymentType, fetchRidersList, finalCreatedeliveries, notifyRider } from 'pages/api/api';
import { OpenToast } from 'components/third-party/OpenToast';
import { useMutation, useQuery } from '@tanstack/react-query';
import Loader from 'components/Loader';
import CircularLoader from 'components/CircularLoader';
import { Empty } from 'antd';
import HoverSocialCard from 'components/cards/statistics/HoverSocialCard';
import { DashboardFilled, DashboardOutlined, OpenAIFilled } from '@ant-design/icons';
import { MdDirectionsBike } from 'react-icons/md';
import { FaMapLocationDot } from 'react-icons/fa6';
import { HiOutlineArrowLeft } from 'react-icons/hi';
import { IoReload } from 'react-icons/io5';
import CheckBoxOutlineBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank';
import CheckBoxIcon from '@mui/icons-material/CheckBox';
import { GiProfit } from 'react-icons/gi';
import { ArrowRightAltOutlined } from '@mui/icons-material';
import LocalShippingIcon from '@mui/icons-material/LocalShipping';
import WarningIcon from '@mui/icons-material/Warning';
import BoltIcon from '@mui/icons-material/Bolt';
import CurrencyRupeeIcon from '@mui/icons-material/CurrencyRupee';
import SavingsIcon from '@mui/icons-material/Savings';
import EnergySavingsLeafIcon from '@mui/icons-material/EnergySavingsLeaf';
import DirectionsBikeOutlinedIcon from '@mui/icons-material/DirectionsBikeOutlined';
import DownloadOutlinedIcon from '@mui/icons-material/DownloadOutlined';
import { exportToExcel } from 'components/exportToExcel';
import CSVExport from 'components/third-party/ReactTable';
var utc = require('dayjs/plugin/utc');
dayjs.extend(utc);
const OptimisedOrderPreview = () => {
const theme = useTheme();
const navigate = useNavigate();
const location = useLocation();
console.log('location.state', location.state);
const [rider, setRider] = useState(null);
const [payment, setPayment] = useState(null);
const [finaldeliveryList, setFinalDeliveryList] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [deliverylist, setDeliverylist] = useState(location.state?.deliverylist);
const [zoneData, setZoneData] = useState(location?.state?.zoneData);
const [metaData, setMetaData] = useState(location.state?.metaData);
const riderToken = location.state?.riderToken;
const appId = location.state?.appId;
const aiMode = location.state?.aiMode;
const reassignOrders = location.state?.reassignOrders;
const [csvExportData, setCsvExportData] = useState([]);
useEffect(() => {
console.log('called');
console.log('aiMode', aiMode);
console.log('riderToken', riderToken);
console.log('zoneData', zoneData);
console.log('metaData', metaData);
console.log('reassignOrders', reassignOrders);
}, [zoneData, metaData, deliverylist]);
useEffect(() => {
const filtered = finaldeliveryList.map((item) => ({
zone_name: item.zone_name,
ordernotes: item.ordernotes,
rider: item.rider,
step: item.step,
ordertype: item.ordertype,
orderamount: item.orderamount,
riderkms: item.riderkms,
cumulativekms: item.cumulativekms,
baseprice: item.baseprice,
minkm: item.minkm,
priceperkm: item.priceperkm,
kms: item.kms,
actualkms: item.actualkms,
rider_charge: item.rider_charge,
deliveryamt: item.deliveryamt,
deliverycharges: item.deliverycharges,
profit: item.profit
}));
setCsvExportData(filtered);
console.log('csvExportData', filtered);
}, [finaldeliveryList]);
useEffect(() => {
if (!deliverylist?.length) return;
const updateDeliveryAmtList = deliverylist.map((list) => {
const cumulativeKms = Number(list.cumulativekms || 0);
const minKm = Number(list.minkm || 0);
const basePrice = Number(list.baseprice || 0);
const pricePerKm = Number(list.priceperkm || 0);
if (cumulativeKms <= minKm) {
return {
...list,
deliveryamt: basePrice
};
}
return {
...list,
deliveryamt: (cumulativeKms - minKm) * pricePerKm + basePrice
};
});
setFinalDeliveryList(updateDeliveryAmtList);
console.log('finaldeliveryList', updateDeliveryAmtList);
}, [deliverylist]);
// ==============================|| fetchPaymentType ||============================== //
const {
data: paymentModes = [],
isLoading: paymentModesLoading,
isError: paymentModesError,
error: paymentModesErrorMessage
} = useQuery({
queryKey: ['paymentmodes'],
queryFn: fetchPaymentType
});
// ==============================|| fetchRidersList ||============================== //
const {
data: ridersList = [],
isLoading: ridersListLoading,
isError: ridersListError,
error: ridersListErrorMessage,
refetch: ridersListRefetch
} = useQuery({
queryKey: ['ridersList', appId], // Unique key for caching & re-fetching
queryFn: fetchRidersList,
enabled: appId !== 0 // Ensures query runs only when appId is valid
});
const getRiderName = async (userid) => {
await ridersList.map((rider) => {
if (rider.userid == userid) {
return rider.firstname;
}
});
};
// ======================================================= || notifyRiderMutation || =======================================================
const notifyRiderMutation = useMutation({
mutationFn: notifyRider, // Using the separate function
onSuccess: () => {
OpenToast('Notification sent Successfully', 'success', 2000);
},
onError: (error) => {
OpenToast(error.message, 'error', 2000);
}
});
const createFinalDeliveryMutation = useMutation({
mutationFn: finalCreatedeliveries, // for optimised delivery create
onSuccess: (data, variables) => {
console.log('data', data);
console.log('varialbles', variables);
notifyRiderMutation.mutate(rider.userfcmtoken || riderToken); // Call notifyRider after success
if (data.status == 'accepted') {
OpenToast('Delivery Created Successfully', 'success', 2000);
}
},
onError: (error) => {
OpenToast(error.message, 'error', 4000);
},
onSettled: () => {
setTimeout(() => {
setIsLoading(false);
navigate('/nearle/deliveries');
}, 2000);
}
});
const handleFinalCreateDelivery = async () => {
// console.log('Final delivery list:', finaldeliveryList);
setIsLoading(true);
createFinalDeliveryMutation.mutate({
deliveries: finaldeliveryList
});
};
const createDeliveryMutation = useMutation({
// 0 -> manual rider assign 1 -> ai rider assign
mutationFn: createAutomationDeliveries,
onSuccess: (data, variables) => {
console.log('data', data);
console.log('varialbles', variables);
OpenToast('Orders Optimised Successfully', 'success', 2000);
// notifyRiderMutation.mutate(variables?.riderToken); // Call notifyRider after success
setZoneData(data?.zones);
setDeliverylist(data?.details);
setMetaData(data?.meta);
setIsLoading(false);
},
onError: (error) => {
OpenToast(error.message, 'error', 4000);
setIsLoading(false);
},
onSettled: () => {
setIsLoading(false);
}
});
const tuningTypes = [
{
tuneid: 1,
type: 'Rider'
},
{
tuneid: 2,
type: 'Pickup'
},
{
tuneid: 3,
type: 'Zone'
}
];
return (
<Fragment>
{
<Backdrop
sx={{
color: '#fff',
zIndex: (theme) => theme.zIndex.drawer + 1
}}
open={paymentModesLoading || ridersListLoading || isLoading} // when loader = true, backdrop covers the page
>
<CircularLoader color="inherit" />
</Backdrop>
}
{(paymentModesLoading || ridersListLoading || isLoading) && (
<>
<Loader />
<CircularLoader />
</>
)}
<Stack direction="row" alignItems="center" justifyContent="space-between" sx={{}}>
<Stack direction="row" alignItems="center" spacing={1}>
<Tooltip title="Back to orders" placement="top">
<IconButton
onClick={() => navigate('/nearle/orders')}
sx={{
bgcolor: 'action.hover',
'&:hover': { bgcolor: 'action.selected' }
}}
>
<HiOutlineArrowLeft size={20} />
</IconButton>
</Tooltip>
<Typography variant="h3" fontWeight={600}>
Assign Orders
</Typography>
</Stack>
{/* <Button
variant="contained"
color="primary"
startIcon={<IoReload />}
onClick={() => {
setIsLoading(true);
createDeliveryMutation.mutate({ deliveries: reassignOrders });
}}
>
Tuning
</Button> */}
<Autocomplete
multiple
id="checkboxes-tags-demo"
options={tuningTypes}
disableCloseOnSelect
getOptionLabel={(option) => option.type}
renderOption={(props, option, { selected }) => {
const { id, ...optionProps } = props;
const SelectionIcon = selected ? CheckBoxIcon : CheckBoxOutlineBlankIcon;
return (
<li key={id} {...optionProps}>
<SelectionIcon fontSize="small" style={{ marginRight: 8, padding: 9, boxSizing: 'content-box' }} />
{option.type}
</li>
);
}}
style={{ width: 400 }}
renderInput={(params) => (
<TextField
{...params}
sx={{
'& .MuiInputBase-input': {
lineHeight: '1.5' // 👈 important
}
}}
label="Hyper Tuning"
// placeholder="Favorites"
/>
)}
/>
{/* <Button
variant="outlined"
size="small"
startIcon={<DownloadOutlinedIcon />}
onClick={() => {
exportToExcel(finaldeliveryList, 'users', 'UsersData');
}}
>
Export Report
</Button> */}
<CSVExport
data={csvExportData}
filename={`Orders_Detail_${dayjs().format('YYYY-MM-DD_HHmmss')}.csv`}
label=" CSV"
style={{ m: 1 }}
/>
</Stack>
<Stack sx={{ my: 2 }}>
<Grid container spacing={2}>
<Grid item xs={6} sm={6} md={3}>
<HoverSocialCard
secondary={metaData?.total_orders}
primary={'Orders'}
percentage={<DashboardFilled />}
color={theme.palette.success.main}
sx={{ cursor: 'pointer' }}
/>
</Grid>
<Grid item xs={6} sm={6} md={3}>
<HoverSocialCard
secondary={metaData?.utilized_riders}
primary={'Riders'}
percentage={<MdDirectionsBike />}
color={theme.palette.warning.main}
/>
</Grid>
<Grid item xs={6} sm={6} md={3}>
<HoverSocialCard
secondary={zoneData?.length}
primary={'Zones'}
percentage={<FaMapLocationDot />}
color={theme.palette.info.main}
/>
</Grid>
<Grid item xs={6} sm={6} md={3}>
<HoverSocialCard
secondary={metaData?.total_profit}
primary={'Profit'}
percentage={<GiProfit />}
color={theme.palette.error.main}
/>
</Grid>
</Grid>
</Stack>
<MainCard content={false}>
{zoneData?.map((zone, index) => {
const riders = zone?.riders.flatMap((rider) => rider);
const orders = zone?.riders?.flatMap((rider) => rider.orders);
return (
<Accordion key={index}>
<AccordionSummary>
<Stack direction="row" alignItems="center" spacing={2} sx={{ m: 2 }}>
<Typography variant="h5" fontWeight={1000}>
Zone {index + 1} :
</Typography>
<Chip label={zone.zone_name} color="primary" variant="light" size="small" sx={{ minWidth: 100 }} />
<Tooltip title={`Orders`} placement="top">
<Badge badgeContent={zone.total_orders} color="secondary">
<DashboardOutlined style={{ fontSize: 20 }} />
</Badge>
</Tooltip>
<Tooltip title={`Riders`} placement="top">
<Badge color="secondary" badgeContent={zone.active_riders_count}>
<DirectionsBikeOutlinedIcon style={{ fontSize: 20 }} />
</Badge>
</Tooltip>
</Stack>
</AccordionSummary>
<AccordionDetails>
<TableContainer sx={{ p: 0, m: 0 }}>
<Table stickyHeader>
<TableHead>
<TableRow>
<TableCell>#</TableCell>
{/* <TableCell>Tenant</TableCell> */}
<TableCell>Order Location</TableCell>
<TableCell>Pickup</TableCell>
<TableCell>Delivery</TableCell>
<TableCell>Notes</TableCell>
<TableCell>Rider</TableCell>
<TableCell align="center">Type</TableCell>
<TableCell align="center">Profit</TableCell>
<TableCell align="center">Charges</TableCell>
<TableCell align="center">KMS</TableCell>
</TableRow>
</TableHead>
<TableBody>
{orders.map((val, i) => (
<Fragment key={i}>
<TableRow sx={{}}>
<TableCell>
<Typography> {i + 1}</Typography>
</TableCell>
{/* <TableCell>
<Tooltip title={val.tenantaddress}>
<Typography variant="body1" noWrap>
{val.tenantname}
</Typography>
<Typography noWrap sx={{ fontSize: '11px' }}>
{val.tenantsuburb}
<br />
</Typography>
<Typography noWrap variant="body2">
{val.applocation}
</Typography>
</Tooltip>
</TableCell> */}
<TableCell align="left">
<Tooltip title={val.locationaddress} placement="top">
<Typography variant="body1" noWrap>
{`${val.locationname}-(${val.locationsuburb})`}
</Typography>
</Tooltip>
<Tooltip title="Order Id">
<Typography variant="body2" noWrap>
{val.orderid}
</Typography>
</Tooltip>
<Stack display={'flex'} flexDirection={'row'} gap={3} sx={{ cursor: 'pointer' }}>
<Tooltip title="Pickup Time">
<Typography noWrap sx={{ fontSize: '12px' }}>
{dayjs(val.pickupslot).format('DD/MM/YYYY')}
</Typography>
<Chip
size="small"
label={dayjs(val.pickupslot).format('hh:mm A')}
variant="light"
sx={{ color: '#0a803b', bgcolor: '#c3f3c7' }}
/>
</Tooltip>
<ArrowRightAltOutlined />
<Tooltip title="Estimated Delivery time">
<Typography noWrap sx={{ fontSize: '12px' }}>
{dayjs(val.expecteddeliverytime).format('DD/MM/YYYY')}
</Typography>
<Chip
size="small"
label={dayjs(val.expecteddeliverytime).format('hh:mm A')}
variant="light"
sx={{ color: '#DD2C00', background: '#FBE9E7' }}
/>
</Tooltip>
</Stack>
</TableCell>
<TableCell align="left">
<Stack direction={'row'} spacing={1}>
<Stack direction="column">
<Typography variant="caption">{val.pickupcustomer}</Typography>
<Typography variant="caption">{val.pickupcontactno}</Typography>
<Tooltip title={val.pickupaddress}>
<Typography sx={{ cursor: 'pointer' }} variant="caption">
{val.pickupsuburb || val.pickupaddress.slice(0, 20)}
</Typography>
</Tooltip>
</Stack>
</Stack>
</TableCell>
<TableCell align="left">
<Stack direction={'row'} spacing={1}>
<Stack direction="column">
<Typography variant="caption">{val.deliverycustomer}</Typography>
<Typography variant="caption">{val.deliverycontactno}</Typography>
<Tooltip title={val.deliveryaddress}>
<Typography sx={{ cursor: 'pointer' }} variant="caption">
{val.deliverysuburb || val.deliveryaddress.slice(0, 20)}
</Typography>
</Tooltip>
</Stack>
</Stack>
</TableCell>
<TableCell align="left">
<Typography variant="caption" sx={{ whiteSpace: 'nowrap' }}>
{val.ordernotes}
</Typography>
</TableCell>
<TableCell align="left">
<Typography variant="caption" sx={{ whiteSpace: 'nowrap' }}>
{val.rider}
</Typography>
<br />
<Typography variant="caption">ID : {val.userid}</Typography>
</TableCell>
<TableCell align="center">
<Chip
size="small"
label={val.ordertype}
icon={
val.ordertype === 'Economy' ? (
<EnergySavingsLeafIcon />
) : val.ordertype === 'Risky' ? (
<WarningIcon />
) : (
<BoltIcon />
)
}
color={val.ordertype === 'Economy' ? 'success' : val.ordertype === 'Risky' ? 'error' : 'primary'}
variant="light"
/>
</TableCell>
<TableCell align="center">
<Chip
size="small"
label={`${parseFloat(val?.profit).toFixed(2)}`}
variant="light"
sx={{ color: '#009688', bgcolor: '#b2dfdb' }}
/>
</TableCell>
<TableCell align="center">
<Stack display={'flex'} flexDirection={'column'} gap={1} sx={{ cursor: 'pointer' }}>
<Tooltip title="Charges" placement="top">
<Chip
size="small"
label={`${val.deliverycharge.toFixed(2)} `}
variant="light"
sx={{ color: '#0074e7', bgcolor: '#cce3fa' }}
/>
</Tooltip>
{/* <Tooltip title="Amount" placement="left">
<Chip size="small" label={`₹ ${val.deliveryamt.toFixed(2)} `} color="success" variant="light" />
</Tooltip> */}
</Stack>
</TableCell>
<TableCell align="center">
<Stack display={'flex'} flexDirection={'column'} gap={1} sx={{ cursor: 'pointer' }}>
<Tooltip title="KMS" placement="top">
<Chip size="small" label={`${val.kms} km`} color="error" variant="light" />
</Tooltip>
<Tooltip title="Cumulative Kms" placement="right">
<Chip size="small" label={`${val.cumulativekms} km`} color="success" variant="light" />
</Tooltip>
</Stack>
</TableCell>
</TableRow>
</Fragment>
))}
</TableBody>
</Table>
</TableContainer>
</AccordionDetails>
</Accordion>
);
})}
<Divider />
<Stack display={'flex'} flexDirection={'row'} gap={2} alignItems={'center'} justifyContent={'end'} sx={{ p: 2 }}>
<Button
sx={{}}
variant="contained"
color="secondary"
startIcon={<ArrowBackIcon />}
onClick={() => {
navigate('/nearle/orders');
}}
>
Back
</Button>
<Button sx={{ my: 2 }} variant="contained" disabled={aiMode === 0 && (!rider || !payment)} onClick={handleFinalCreateDelivery}>
Assign Orders
</Button>
</Stack>
</MainCard>
</Fragment>
);
};
export default OptimisedOrderPreview;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,91 @@
import React, { useEffect, useRef } from 'react';
import { LoadScriptNext, GoogleMap } from '@react-google-maps/api';
const containerStyle = {
width: '100%',
height: '90vh'
};
const MapWithRouteGoogle = ({ coordinates, additionalProps, setMapOpen }) => {
const mapRef = useRef(null);
/** Convert coordinates to numbers */
const numericCoordinates = coordinates
.map((c) => {
const lat = Number(c.lat);
const lng = Number(c.lng);
return isNaN(lat) || isNaN(lng) ? null : { lat, lng };
})
.filter(Boolean);
if (numericCoordinates.length < 2) {
return <div>No route data available</div>;
}
const start = numericCoordinates[0];
const end = numericCoordinates[numericCoordinates.length - 1];
/** Map loaded callback */
const onMapLoad = (map) => {
// draw markers
new window.google.maps.Marker({
position: start,
map,
label: 'S',
title: `Start: ${additionalProps?.riderStart}`
});
new window.google.maps.Marker({
position: end,
map,
label: 'E',
title: `End: ${additionalProps?.riderEnd}`
});
// draw rider route (point-to-point)
const route = new window.google.maps.Polyline({
path: numericCoordinates,
geodesic: false,
strokeColor: '#1A73E8',
strokeOpacity: 1.0,
strokeWeight: 4
});
route.setMap(map);
// auto fit
const bounds = new window.google.maps.LatLngBounds();
numericCoordinates.forEach((p) => bounds.extend(p));
map.fitBounds(bounds);
};
return (
<>
<button
onClick={() => setMapOpen(false)}
style={{
position: 'absolute',
top: 10,
right: 10,
zIndex: 999,
padding: '6px 12px',
background: '#1A73E8',
color: 'white',
borderRadius: 6,
cursor: 'pointer',
border: 'none'
}}
>
Close
</button>
<LoadScriptNext googleMapsApiKey={process.env.REACT_APP_GOOGLE_MAPS_API_KEY}>
<GoogleMap mapContainerStyle={containerStyle} center={start} zoom={14} onLoad={onMapLoad}>
{/* Polyline and markers added via onLoad */}
</GoogleMap>
</LoadScriptNext>
</>
);
};
export default MapWithRouteGoogle;

View File

@@ -0,0 +1,76 @@
import { Button } from '@mui/material';
import { LoadScriptNext, GoogleMap, Marker, OverlayView } from '@react-google-maps/api';
const containerStyle = {
width: '100%',
height: 'calc(100vh - 150px)'
};
export default function RiderLocationMap({ riderLocations }) {
console.log('riderLocations', riderLocations);
const center = {
lat: Number(riderLocations?.[0]?.latitude || 11.0056),
lng: Number(riderLocations?.[0]?.longitude || 76.9661)
};
const GreenIcon = {
url: 'https://cdn.rawgit.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-green.png',
scaledSize: new window.google.maps.Size(25, 41),
anchor: new window.google.maps.Point(12, 41)
};
const RedIcon = {
url: 'https://cdn.rawgit.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-red.png',
scaledSize: new window.google.maps.Size(25, 41),
anchor: new window.google.maps.Point(12, 41)
};
return (
<LoadScriptNext googleMapsApiKey={process.env.REACT_APP_GOOGLE_MAPS_API_KEY}>
<GoogleMap mapContainerStyle={containerStyle} zoom={12} center={center}>
{riderLocations &&
riderLocations?.map((r, index) => {
const lat = Number(r.latitude);
const lng = Number(r.longitude);
return (
<div key={index}>
{/* Marker */}
<Marker
position={{ lat, lng }}
icon={r.status == 'active' ? GreenIcon : RedIcon}
label={{
fontSize: '14px',
fontWeight: 'bold'
}}
/>
<OverlayView position={{ lat, lng }} mapPaneName={OverlayView.OVERLAY_LAYER}>
<div
style={{
background: 'none',
color: 'green',
padding: '2px 8px',
borderRadius: '4px',
fontSize: '12px',
fontWeight: 600,
whiteSpace: 'nowrap',
transform: 'translate(-50%, -140%)',
pointerEvents: 'none',
ml: 20
}}
>
<Button variant="contained" color="primary" size="small">
{` ${r.username} `}
{/* <br /> */}
{/* {`${r.contactno || '##### ##### '} `} */}
<br />
{`(${r.orderid || ''}) `}
</Button>
</div>
</OverlayView>
</div>
);
})}
</GoogleMap>
</LoadScriptNext>
);
}

View File

@@ -0,0 +1,69 @@
import React, { useEffect, useMemo, useRef } from 'react';
import { GoogleMap, Polyline, Marker, useJsApiLoader } from '@react-google-maps/api';
const containerStyle = {
width: '100%',
height: '100%'
};
export default function RidersRoutes({ details }) {
const mapRef = useRef(null);
const { isLoaded } = useJsApiLoader({
googleMapsApiKey: process.env.REACT_APP_GOOGLE_MAPS_KEY
});
// Convert dataset
const routePath = useMemo(
() =>
details?.map((p) => ({
lat: Number(p.latitude),
lng: Number(p.longitude)
})),
[details]
);
const bikeIcon = {
path: 'M12 2c-2.2 0-4 1.8-4 4v3H5l-1 2h2l3.6 7.59c.34.58.96.94 1.64.94h2.52c.68 0 1.3-.36 1.64-.94L19 11h2l-1-2h-3V6c0-2.2-1.8-4-4-4z',
fillColor: '#9c27b0', // 🔥 purple
fillOpacity: 1,
strokeWeight: 0,
scale: 1.4,
anchor: new window.google.maps.Point(12, 24)
};
// Auto fit bounds
useEffect(() => {
if (!mapRef.current || routePath.length === 0) return;
const bounds = new window.google.maps.LatLngBounds();
routePath.forEach((p) => bounds.extend(p));
mapRef.current.fitBounds(bounds);
}, [routePath]);
if (!isLoaded) return <div>Loading map...</div>;
return (
<GoogleMap mapContainerStyle={containerStyle} onLoad={(map) => (mapRef.current = map)} center={routePath[0]} zoom={16}>
{/* Route line */}
<Polyline
path={routePath}
options={{
strokeColor: '#196fd2',
strokeOpacity: 0.9,
strokeWeight: 5
}}
/>
{/* Start marker */}
<Marker
position={routePath[0]}
icon={{
url: 'http://maps.google.com/mapfiles/ms/icons/green-dot.png'
}}
/>
{/* End marker */}
<Marker position={routePath[routePath.length - 1]} icon={bikeIcon} />
</GoogleMap>
);
}

View File

@@ -0,0 +1,173 @@
import React, { useEffect, useRef, useState } from 'react';
import { MapContainer, TileLayer, Marker, Polyline, Tooltip } from 'react-leaflet';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
import dayjs from 'dayjs';
import { Chip, Stack, Typography, Box } from '@mui/material';
import { CloseCircleOutlined } from '@ant-design/icons';
import { useTheme } from '@mui/material/styles';
import CircularLoader from 'components/CircularLoader';
var utc = require('dayjs/plugin/utc');
dayjs.extend(utc);
// Start marker
const startIcon = new L.Icon({
iconUrl: 'https://cdn.rawgit.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-green.png',
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
shadowSize: [41, 41]
});
// End marker
const endIcon = new L.Icon({
iconUrl: 'https://cdn.rawgit.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-red.png',
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
shadowSize: [41, 41]
});
const MapWithRoute = ({ coordinates, additionalProps, order, setMapOpen }) => {
console.log('additionalProps', additionalProps);
const mapRef = useRef(null);
const theme = useTheme();
const [routePoints, setRoutePoints] = useState([]);
const [loading, setLoading] = useState(false);
// Fit the map to bounds
useEffect(() => {
if (mapRef.current && coordinates.length > 0) {
const bounds = [
[Math.min(...coordinates.map((c) => c.lat)), Math.min(...coordinates.map((c) => c.lng))],
[Math.max(...coordinates.map((c) => c.lat)), Math.max(...coordinates.map((c) => c.lng))]
];
mapRef.current.fitBounds(bounds);
}
}, [coordinates]);
// Fetch OSRM Route → REAL ROAD ROUTE
useEffect(() => {
const getOSRMRoute = async () => {
setLoading(true);
// FIX: If only one coordinate, stop loader and exit
if (coordinates.length < 2) {
setRoutePoints([]); // no route
setLoading(false);
return;
}
const start = coordinates[0];
const end = coordinates[coordinates.length - 1];
const url = `https://router.project-osrm.org/route/v1/driving/${start.lng},${start.lat};${end.lng},${end.lat}?overview=full&geometries=geojson`;
try {
const res = await fetch(url);
const data = await res.json();
if (data.routes?.length) {
const points = data.routes[0].geometry.coordinates.map(([lng, lat]) => ({
lat,
lng
}));
setRoutePoints(points);
}
} catch (err) {
console.error('OSRM Error:', err);
} finally {
setLoading(false); // ALWAYS STOP LOADING
}
};
getOSRMRoute();
}, [coordinates]);
if (!coordinates || coordinates.length === 0) return null;
const start = coordinates[0];
const end = coordinates[coordinates.length - 1];
const center = coordinates[Math.floor(coordinates.length / 2)];
const InfoItem = ({ label, value }) => (
<Stack direction="row" spacing={1} alignItems="center">
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
{label}:
</Typography>
<Chip label={value || '0.00'} color="primary" sx={{ fontWeight: 700 }} />
</Stack>
);
return (
<Box sx={{ width: '100%', height: '100vh', position: 'relative', overflow: 'hidden' }}>
{loading && <CircularLoader />}
{/* CLOSE BUTTON */}
<Chip
label="Close"
icon={<CloseCircleOutlined style={{ fontSize: 18 }} />}
onClick={() => setMapOpen(false)}
sx={{
position: 'absolute',
top: 12,
right: 12,
zIndex: 2000,
bgcolor: theme.palette.error.main,
color: '#fff',
fontWeight: 600,
borderRadius: '12px',
px: 1.5,
py: 0.5,
boxShadow: theme.shadows[4],
cursor: 'pointer',
'& .MuiChip-icon': { color: '#fff' }
}}
/>
{/* MAP */}
<MapContainer center={center} zoom={14} scrollWheelZoom style={{ height: '100%', width: '100%' }} ref={mapRef}>
<TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
{/* START MARKER */}
<Marker position={start} icon={startIcon}>
<Tooltip direction="bottom">{`Pickup: ${dayjs(additionalProps.riderStart).format('DD-MM-YYYY hh:mm A')}`}</Tooltip>
</Marker>
{/* END MARKER */}
<Marker position={end} icon={endIcon}>
<Tooltip direction="bottom">{`Drop: ${dayjs(additionalProps.riderEnd).format('DD-MM-YYYY hh:mm A')}`}</Tooltip>
</Marker>
{/* REAL OSRM ROUTE */}
{routePoints.length > 0 && <Polyline positions={routePoints} pathOptions={{ color: 'blue', weight: 5 }} />}
</MapContainer>
{/* BOTTOM DETAILS */}
<Box
sx={{
position: 'absolute',
bottom: 0,
width: '100%',
bgcolor: 'rgba(255,255,255,0.96)',
p: 2,
boxShadow: theme.shadows[3],
zIndex: 1500
}}
>
<Stack direction="row" flexWrap="wrap" rowGap={1.5} columnGap={3} alignItems="center">
<InfoItem label="Tenant" value={order?.tenantname} />
<InfoItem label="Rider" value={order?.ridername} />
<InfoItem label="Pickup" value={order?.pickupcustomer} />
<InfoItem label="Drop" value={order?.deliverycustomer} />
<InfoItem label="Kms" value={order?.kms} />
<InfoItem label="Actual Kms" value={order?.actualkms} />
<InfoItem label="Rider Kms" value={order?.riderkms} />
</Stack>
</Box>
</Box>
);
};
export default MapWithRoute;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,935 @@
import { React, useState, useEffect, useRef } from 'react';
import TitleCard from 'pages/titleCard';
import axios from 'axios';
import { useQuery } from '@tanstack/react-query';
import { Empty } from 'antd';
// material-ui
import {
Box,
Divider,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Typography,
Stack,
IconButton,
Tooltip,
Chip,
Autocomplete,
TextField
} from '@mui/material';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
import dayjs from 'dayjs';
var utc = require('dayjs/plugin/utc');
dayjs.extend(utc);
import { CalendarMonth } from '@mui/icons-material';
import { getreportlocationsummary, getreportsummary, gettenantlocations, getTenants } from 'pages/api/api';
import MainCard from 'components/MainCard';
import Loader from 'components/Loader';
import { useTheme } from '@mui/material/styles';
import DateFilterDialog from 'components/DateFilterDialog';
import LocationAutocomplete from 'components/nearle_components/LocationAutocomplete';
import { OpenToast } from 'components/third-party/OpenToast';
import { OrdersTableSkeleton } from '../orders/OrdersTableSkeleton';
function formatNumberToRupees(value) {
return new Intl.NumberFormat('en-IN', {
style: 'currency',
currency: 'INR',
minimumFractionDigits: 2
}).format(value);
}
// ==============================|| MUI TABLE - ENHANCED ||============================== //
export default function OrdersReport() {
// const [rows, setRows] = useState([]);
const theme = useTheme();
const locationRef = useRef(null);
const tenantRef = useRef(null);
const [appId, setAppId] = useState(0);
const [startdate, setStartdate] = useState(dayjs().format('YYYY-MM-DD'));
const [enddate, setEnddate] = useState(dayjs().format('YYYY-MM-DD'));
const [locaName, setLocoName] = useState('All');
const [open, setOpen] = useState(false);
const [openRow, setOpenRow] = useState(null);
const [datestatus, setDatestatus] = useState('Today');
const [total, settotal] = useState(0);
const [totalOrders, settotalOrders] = useState(0);
const [totalOrderPend, setTotalOrderPend] = useState(0);
const [totalOrderComplete, setTotalOrderComplete] = useState(0);
const [totalOrderCancel, setTotalOrderCancel] = useState(0);
const [totalDeliPend, setTotalDeliPend] = useState(0);
const [totalDeliComplete, setTotalDeliComplete] = useState(0);
const [totalDeliCancel, setTotalDeliCancel] = useState(0);
const [ridersdata, setRidersdata] = useState([]);
const userid = localStorage.getItem('userid');
const [loading, setLoading] = useState(false);
const [locationid, setLocationid] = useState(0);
const [tenantid, setTenantid] = useState(0);
const [tenantValue, setTenantValue] = useState(null);
const [locationValue, setLocationValue] = useState(null);
const [page, setPage] = useState(0);
useEffect(() => {
console.log('openRow', openRow);
}, [openRow]);
// to clear the tenant and location and rider autocomplete
useEffect(() => {
setTenantid(0);
setTenantValue(null);
setLocationid(0);
setLocationValue(null);
setOpenRow(null);
}, [appId]);
// to clear the location and rider autocomplete
useEffect(() => {
setLocationid(0);
setLocationValue(null);
setOpenRow(null);
}, [tenantid]);
// table header
const headCells = [
{
id: 's.no',
numeric: false,
disablePadding: true,
label: '#',
rowSpan: 2,
colSpan: 1,
bgcolor: '#f3e5f5'
},
{
id: 'clients',
numeric: 'true',
disablePadding: false,
label: tenantid ? 'Location' : 'Tenant',
rowSpan: 2,
colSpan: 1,
bgcolor: '#f3e5f5'
},
{
id: 'all',
numeric: 'center',
disablePadding: false,
label: 'All',
rowSpan: 2,
colSpan: 1,
bgcolor: '#f3e5f5'
},
{
id: 'orders',
numeric: 'center',
disablePadding: false,
label: 'orders',
rowSpan: 1,
colSpan: 3,
bgcolor: '#ffcdd2'
},
{
id: 'deliveries',
numeric: 'center',
disablePadding: false,
label: 'deliveries',
rowSpan: 1,
colSpan: 3,
bgcolor: '#f8bbd0'
},
{
id: 'Charges',
numeric: 'center',
disablePadding: false,
label: 'Collection Amt',
rowSpan: 2,
colSpan: 1,
bgcolor: '#f3e5f5'
},
{
id: 'kilometer',
numeric: 'center',
disablePadding: false,
label: 'Kms/Actual Kms',
rowSpan: 2,
colSpan: 1,
bgcolor: '#f3e5f5'
},
{
id: 'Amount',
numeric: 'center',
disablePadding: true,
label: 'Amount',
rowSpan: 2,
colSpan: 1,
bgcolor: '#f3e5f5'
},
{
id: 'action',
numeric: 'center',
disablePadding: true,
label: 'Action',
rowSpan: 2,
colSpan: 1,
bgcolor: '#f3e5f5'
}
];
const headCells1 = [
{
id: 'pending',
numeric: false,
disablePadding: false,
label: 'pending',
rowSpan: 1,
colSpan: 1,
bgcolor: '#ffcdd2'
},
{
id: 'cancelled',
numeric: false,
disablePadding: false,
label: 'cancelled',
rowSpan: 1,
colSpan: 1,
bgcolor: '#ffcdd2'
},
{
id: 'completed',
numeric: false,
disablePadding: false,
label: 'completed',
rowSpan: 1,
colSpan: 1,
bgcolor: '#ffcdd2'
},
{
id: 'pending',
numeric: false,
disablePadding: false,
label: 'pending',
rowSpan: 1,
colSpan: 1,
bgcolor: '#f8bbd0'
},
{
id: 'cancelled',
numeric: false,
disablePadding: false,
label: 'cancelled',
rowSpan: 1,
colSpan: 1,
bgcolor: '#f8bbd0'
},
{
id: 'completed',
numeric: false,
disablePadding: false,
label: 'completed',
rowSpan: 1,
colSpan: 1,
bgcolor: '#f8bbd0'
}
];
const getColorByValue = (value) => {
return Number(value) !== 0 ? 'red' : 'inherit';
};
const coloredCell = (value) => <Typography sx={{ color: getColorByValue(value) }}>{value}</Typography>;
// ==============================|| MUI TABLE - HEADER ||============================== //
function EnhancedTableHead() {
return (
<TableHead>
<TableRow>
{headCells.map((headCell) => (
<TableCell
key={headCell.id}
align={headCell.numeric == 'center' ? 'center' : headCell.numeric == 'true' ? 'left' : 'right'}
padding={headCell.disablePadding ? 'none' : 'normal'}
rowSpan={headCell.rowSpan}
colSpan={headCell.colSpan}
sx={{ bgcolor: headCell.bgcolor }}
>
{headCell.label}
</TableCell>
))}
</TableRow>
<TableRow>
{headCells1.map((headCell) => (
<TableCell
key={headCell.id}
align={headCell.numeric ? 'right' : 'left'}
padding={headCell.disablePadding ? 'none' : 'normal'}
rowSpan={headCell.rowSpan}
colSpan={headCell.colSpan}
sx={{ bgcolor: headCell.bgcolor }}
>
{headCell.label}
</TableCell>
))}
</TableRow>
</TableHead>
);
}
// ==============================|| fetchOrdersSummary (orders summary)||============================== //
const {
data: rows,
isLoading: isLoadingReports,
isError: isErrorReports, //true or false
error: reportsError
} = useQuery({
queryKey: [appId, tenantid, locationid, startdate, enddate],
queryFn: tenantid ? getreportlocationsummary : getreportsummary
});
// ==============================|| getTenants ||============================== //
const {
data: tenantlist
} = useQuery({
queryKey: ['tenantlist', appId],
queryFn: () => getTenants(appId), // Ensure appId is passed
enabled: appId !== 0 // Ensures query runs only when appId is valid
});
// ==============================|| gettenantlocations ||============================== //
const {
data: locationlist
} = useQuery({
queryKey: ['gettenantlocations', tenantid],
queryFn: () => gettenantlocations(tenantid), // Ensure appId is passed
enabled: tenantid !== 0 // Ensures query runs only when appId is valid
});
// ==============================|| fetchridersummary by tenid (orders summary)||============================== //
// ==============================|| getuserreportsummary by tenid (orders summary)||============================== //
const getuserreportsummary = async (tenantid) => {
setLoading(true);
try {
const riderRes = await axios.get(
`${process.env.REACT_APP_URL}/deliveries/getuserreportsummary/?&tenantid=${tenantid}&fromdate=${startdate}&todate=${enddate}`
);
console.log('riderRes', riderRes.data.details);
setRidersdata(riderRes.data.details);
} catch (error) {
console.log('riderRes', error);
} finally {
setLoading(false);
}
};
// ==============================|| getriderlocationsummary by tenid (orders summary)||============================== //
const getriderlocationsummary = async (id) => {
try {
const riderRes = await axios.get(
`${process.env.REACT_APP_URL}/deliveries/getriderlocationsummary/?&tenantid=${tenantid}&locationid=${id}&fromdate=${startdate}&todate=${enddate}`
);
console.log('riderRes', riderRes.data.details);
setRidersdata(riderRes.data.details);
} catch (error) {
console.log('riderRes', error);
}
};
// ==============================|| calculate||============================== //
const calculate = () => {
let calculatedTotal = 0;
let ordersTotal = 0;
let Orderpending = 0;
let OrderComplete = 0;
let OrderCancel = 0;
let deliverypending = 0;
let deliverycomplete = 0;
let deliverycancel = 0;
rows &&
rows.forEach((row) => {
calculatedTotal += row.charges;
ordersTotal += row.totalorders;
Orderpending += row.Orderspending;
OrderComplete += row.orderscompleted;
OrderCancel += row.orderscancelled;
deliverypending += row.deliveriespending;
deliverycomplete += row.deliveriescompleted;
deliverycancel += row.deliveriescancelled;
});
// Update the state after the calculation is done
settotal(calculatedTotal);
settotalOrders(ordersTotal);
setTotalOrderPend(Orderpending);
setTotalOrderComplete(OrderComplete);
setTotalOrderCancel(OrderCancel);
setTotalDeliPend(deliverypending);
setTotalDeliComplete(deliverycomplete);
setTotalDeliCancel(deliverycancel);
};
useEffect(() => {
calculate();
}, [rows]);
// ==============================|| fetchAppLocations ||============================== //
const fetchAppLocations = async () => {
try {
const locationRes = await axios.get(`${process.env.REACT_APP_URL}/partners/getlocations/?userid=${userid}`);
const updatedLocations = [
...locationRes.data.details,
{ locationname: 'All', applocationid: 0 } // Add your new object here
];
console.log('fetchAppLocations', updatedLocations);
setLocations(updatedLocations);
} catch (err) {
console.log('locationRes', err);
}
};
useEffect(() => {
fetchAppLocations();
}, []);
if (isErrorReports) console.log('An error has occurred:(isErrorReports) ' + reportsError.message);
return (
<>
{(loading || isLoadingReports) && (
<>
{/* <CircularLoader /> */}
<Loader />
</>
)}
{/* <Backdrop
sx={{
color: '#fff',
zIndex: (theme) => theme.zIndex.drawer + 1
}}
open={loading || isLoadingReports} // when loader = true, backdrop covers the page
>
<CircularLoader color="inherit" />
</Backdrop> */}
<TitleCard
title="Orders Summary"
secondary={
<LocationAutocomplete
ref={locationRef}
locaName={locaName}
setAppId={setAppId}
setLocoName={setLocoName}
setPage={setPage}
sx={{ minWidth: 280, width: '100%', flex: { custom550: 0 } }}
/>
}
/>
<Stack
display={'flex'}
flexDirection={'row'}
alignItems={'center'}
justifyContent={'space-between'}
flexWrap={'wrap'}
gap={1}
sx={{ border: '1px solid', borderColor: 'bg.main', p: 2 }}
>
<Stack>
{startdate && enddate && (
<Stack direction="row" gap={2} flexWrap={'wrap'}>
<Chip label={`Orders-${datestatus}`} color="primary" variant="light" size="small" />
<Tooltip title="Date Filter" placement="top">
<Chip
label={
<Typography noWrap color="secondary">
{dayjs(startdate).format('DD/MM/YYYY')} - {dayjs(enddate).format('DD/MM/YYYY')}
</Typography>
}
variant="combined"
color="warning"
size="small"
deleteIcon={<CalendarMonth style={{ fontSize: 18 }} />}
onDelete={() => {
setOpen(true);
}}
onClick={() => setOpen(true)}
sx={{ cursor: 'pointer' }}
/>
</Tooltip>
</Stack>
)}
</Stack>
<Stack
sx={{
display: 'flex',
flexDirection: 'row',
gap: 2,
flexWrap: 'wrap',
flexGrow: 1,
justifyContent: { xs: 'start', custom950: 'right' }
}}
>
<Autocomplete
options={tenantlist || []}
value={tenantValue}
sx={{ minWidth: 250, flex: { xs: 1, custom950: 0 } }}
onOpen={(event) => {
if (!appId) {
event.preventDefault();
OpenToast('Please select a your app location first!', 'warning', 3000);
setTimeout(() => {
locationRef.current?.focus();
}, 0);
}
}}
onChange={(e, val, reason) => {
if (reason === 'clear') {
setTenantid(0);
setTenantValue(null);
setLocationid(0);
setLocationValue(null);
} else {
setTenantid(val?.tenantid || 0);
setTenantValue(val);
setLocationid(val.locationid);
setLocationValue(null);
}
}}
renderInput={(params) => <TextField {...params} inputRef={tenantRef} label="Select Tenant" />}
/>
{/* ==================================================== || Location Autocomplete || ==================================================== */}
<Autocomplete
options={locationlist || []}
getOptionLabel={(option) => `${option.locationname} (${option.suburb})` || ''}
value={locationValue}
sx={{ minWidth: 250, flex: { xs: 1, custom950: 0 } }}
onOpen={(event) => {
if (!appId && !tenantid) {
event.preventDefault();
OpenToast('Please select a your 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={(e, val, reason) => {
if (reason === 'clear') {
setLocationid(0);
setLocationValue(null);
} else {
setLocationid(val.locationid || 0);
setLocationValue(val);
}
}}
renderInput={(params) => <TextField {...params} label="Select Location" />}
/>
</Stack>
</Stack>
<MainCard content={false}>
<TableContainer
sx={{
overflow: 'auto',
'&::-webkit-scrollbar': {
width: '12px', // scroll bar width
cursor: 'pointer'
},
'&::-webkit-scrollbar-thumb': {
backgroundColor: theme.palette.primary.main, // thumb color
borderRadius: '8px',
cursor: 'pointer'
},
'&::-webkit-scrollbar-thumb:hover': {
backgroundColor: theme.palette.primary.dark, // hover color
cursor: 'pointer'
},
'&::-webkit-scrollbar-track': {
backgroundColor: theme.palette.primary.lighter,
cursor: 'pointer'
}
}}
>
<Table>
{/* ============================================ ||EnhancedTableHead || ============================================ */}
<EnhancedTableHead />
{/* ============================================ || TableBody || ============================================ */}
<TableBody>
{isLoadingReports && <OrdersTableSkeleton col={8} />}
{rows?.length == 0 && (
<TableRow>
<TableCell colSpan={13} rowSpan={40}>
<Stack width={'100%'} direction={'row'} justifyContent={'center'}>
<Empty />
</Stack>
</TableCell>
</TableRow>
)}
{(rows?.length != 0 || rows) &&
rows?.map((row, index) => (
<>
{/* ============================================ || tablerow 1 || ============================================ */}
<TableRow
key={row.tenantname || row.locationname}
// sx={{
// cursor: openRow === row.tenantname ? 'pointer' : null
// }}
>
<TableCell>{index + 1}</TableCell>
<TableCell align="left">
<Stack spacing={1}>
<Typography sx={{ whiteSpace: 'noWrap' }}>{tenantid ? row.locationname : row.tenantname} </Typography>
<Typography variant="subtitle2">Id : {tenantid ? row.locationid : row.tenantid} </Typography>
</Stack>
</TableCell>
<TableCell align="center">{coloredCell(row.totalorders)}</TableCell>
<TableCell align="center">{coloredCell(row.Orderspending)}</TableCell>
<TableCell align="center">{coloredCell(row.orderscancelled)}</TableCell>
<TableCell align="center">{coloredCell(row.orderscompleted)}</TableCell>
<TableCell align="center">{coloredCell(row.deliveriespending)}</TableCell>
<TableCell align="center">{coloredCell(row.deliveriescancelled)}</TableCell>
<TableCell align="center">{coloredCell(row.deliveriescompleted)}</TableCell>
<TableCell align="center">
{/* <Tooltip title="Cash on Delivery" placement="top">
<Chip
size="small"
label={`₹${Number(row.payondelivery).toFixed(2)}`}
variant="combined"
color={row.payondelivery ? 'error' : 'secondary'}
sx={{
mb: 1,
cursor: 'pointer',
minWidth: 80
}}
/>
</Tooltip>
<br /> */}
{/* <Tooltip title="Pay Later" placement="bottom"> */}
<Chip
size="small"
label={`${Number(row.collectionamt).toFixed(2)}`}
variant="combined"
color={row.collectionamt ? 'warning' : 'secondary'}
sx={{
cursor: 'pointer',
minWidth: 80
}}
/>
{/* </Tooltip> */}
</TableCell>{' '}
<TableCell align="center">
<Tooltip title="kms" placement="top">
<Chip
size="small"
label={`${Number(row.kms).toFixed(2)}`}
color={row.kms ? 'error' : 'secondary'}
variant="combined"
sx={{
mb: 1,
cursor: 'pointer',
minWidth: 80
}}
/>
</Tooltip>
<br />
<Tooltip title="Actual kms" placement="bottom">
<Chip
size="small"
label={Number(row.cumulativekms).toFixed(2)}
color={row.cumulativekms ? 'success' : 'secondary'}
variant="combined"
sx={{
cursor: 'pointer',
minWidth: 80
}}
/>
</Tooltip>
</TableCell>
<TableCell align="right">
<Tooltip title="Delivery Amount" placement="top">
<Chip
size="small"
label={formatNumberToRupees(row.charges >= row.deliveryamt ? row.charges : row.deliveryamt)}
color={row.deliveryamt || row.charges ? 'primary' : 'secondary'}
variant="combined"
sx={{
cursor: 'pointer',
minWidth: 100
}}
/>
</Tooltip>
</TableCell>
<TableCell align="center">
<IconButton
aria-label="expand row"
size="small"
// onClick={() => {
// setRidersdata([]); // yo avoid appending new data list to exiting
// setTimeout(() => {
// openRow !== row.tenantname && (tenantid
// ? getriderlocationsummary(row.locationid)
// : getuserreportsummary(row.tenantid));
// setOpenRow(openRow === row.tenantname ? null : row.tenantname);
// }, 0);
// }}
onClick={() => {
if (tenantid) {
// if tenant selected and shows the location list
const isOpening = openRow !== row.tenantname;
// toggle row
setOpenRow(isOpening ? row.tenantname : null);
if (!isOpening) return; // ❌ closing → don't call API
// clear old data only when opening
setRidersdata([]);
// ✅ call correct API
getriderlocationsummary(row.locationid);
} else {
const isOpening = openRow !== row.locationname;
// toggle row
setOpenRow(isOpening ? row.locationname : null);
if (!isOpening) return; // ❌ closing → don't call API
// clear old data only when opening
setRidersdata([]);
// ✅ call correct API
getuserreportsummary(row.tenantid);
}
}}
sx={{
bgcolor: openRow === row.tenantname ? 'primary.main' : null,
color: openRow === row.tenantname ? 'white' : null,
'&:hover': {
bgcolor: openRow === row.tenantname ? 'primary.main' : '#e1bee7'
}
}}
>
<Tooltip title="View Riders" placement="top">
{tenantid ? (
openRow === row.tenantname ? (
<KeyboardArrowUpIcon />
) : (
<KeyboardArrowDownIcon />
)
) : openRow === row.locationname ? (
<KeyboardArrowUpIcon />
) : (
<KeyboardArrowDownIcon />
)}
</Tooltip>
</IconButton>
</TableCell>
</TableRow>
{/* ============================================ || collapsive row || ============================================ */}
{(tenantid ? openRow === row.tenantname : openRow === row.locationname) && (
<TableRow
// sx={{
// cursor: openRow === row.tenantname ? 'pointer' : null,
// '&:hover': {
// bgcolor: 'white!important'
// }
// }}
>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={15}>
<Box sx={{ margin: 1, border: '1px solid', borderColor: 'secondary.lighter' }}>
<Table size="small">
<TableHead>
<TableRow sx={{ bgcolor: 'primary.lighter' }}>
<TableCell>#</TableCell>
<TableCell>Rider</TableCell>
<TableCell align="center">Orders</TableCell>
<TableCell align="center">Deliveries</TableCell>
<TableCell align="center">Pending</TableCell>
<TableCell align="center">Cancelled</TableCell>
<TableCell align="center">Completed</TableCell>
<TableCell align="center">Collection Amt</TableCell>
<TableCell align="center">kms/Actual kms</TableCell>
<TableCell align="center">Charges</TableCell>
</TableRow>
</TableHead>
<TableBody>
{loading && <OrdersTableSkeleton col={5} />}
{/* {ridersdata?.length == 0 && (
<TableRow>
<TableCell colSpan={13} rowSpan={40}>
<Stack width={'100%'} direction={'row'} justifyContent={'center'}>
<Empty />
</Stack>
</TableCell>
</TableRow>
)} */}
{ridersdata?.map((row, index) => (
<TableRow key={row.tenantname}>
<TableCell>{index + 1}</TableCell>
<TableCell align="left">
<Stack>
<Typography sx={{ whiteSpace: 'noWrap' }}>{` ${row.firstname} ${row.lastname}`}</Typography>
<Typography variant="subtitle2" sx={{ whiteSpace: 'noWrap' }}>
{row.ridercontact}
</Typography>
<Typography variant="subtitle2" sx={{ whiteSpace: 'noWrap' }}>
ID : {row.userid}
</Typography>
</Stack>
</TableCell>
<TableCell align="center">{coloredCell(row.orderscreated)}</TableCell>
<TableCell align="center">{coloredCell(row.totalorders)}</TableCell>
<TableCell align="center">{coloredCell(row.deliveriespending)}</TableCell>
<TableCell align="center">{coloredCell(row.deliveriescancelled)}</TableCell>
<TableCell align="center">{coloredCell(row.deliveriescompleted)}</TableCell>
{/* <TableCell align="center">{coloredCell(row.picked)}</TableCell>
<TableCell align="center">{coloredCell(row.active)}</TableCell>
<TableCell align="center">{coloredCell(row.skipped)}</TableCell>
<TableCell align="center">{coloredCell(row.cancelled)}</TableCell>
<TableCell align="center">{coloredCell(row.delivered)}</TableCell> */}
<TableCell align="center">
{/* <Tooltip title="Cash on Delivery" placement="top">
<Chip
size="small"
label={formatNumberToRupees(row.payondelivery)}
color={row.payondelivery ? 'error' : 'secondary'}
variant="combined"
sx={{
mb: 1,
cursor: 'pointer',
minWidth: 80
}}
/>
</Tooltip>
<br /> */}
{/* <Tooltip title="Paylater" placement="top"> */}
<Chip
size="small"
label={formatNumberToRupees(row.collectionamt)}
color={row.collectionamt ? 'warning' : 'secondary'}
variant="combined"
sx={{
cursor: 'pointer',
minWidth: 80
}}
/>
{/* </Tooltip> */}
</TableCell>
<TableCell align="center">
<Tooltip title="kms" placement="top">
<Chip
size="small"
label={row.kms.toFixed(2)}
color={row.kms ? 'error' : 'secondary'}
variant="combined"
sx={{
mb: 1,
cursor: 'pointer',
minWidth: 80
}}
/>
</Tooltip>
<br />
<Tooltip title="Actual kms" placement="top">
<Chip
size="small"
label={row.cumulativekms.toFixed(2)}
color={row.cumulativekms ? 'success' : 'secondary'}
variant="combined"
sx={{
cursor: 'pointer',
minWidth: 80
}}
/>
</Tooltip>
</TableCell>
<TableCell align="center">
<Tooltip title="Delivery Amount" placement="top">
<Chip
size="small"
label={formatNumberToRupees(row.charges >= row.deliveryamt ? row.charges : row.deliveryamt)}
color={row.deliveryamt || row.charges ? 'primary' : 'secondary'}
variant="combined"
sx={{
cursor: 'pointer',
minWidth: 100
}}
/>
</Tooltip>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Box>
</TableCell>
</TableRow>
)}
</>
))}
{rows?.length != 0 && rows && (
<TableRow sx={{}}>
<TableCell colSpan={2}>
<Typography variant="h5">Total</Typography>
</TableCell>
<TableCell align="center">
<Typography variant="h5">{totalOrders}</Typography>
</TableCell>
<TableCell align="center">
<Typography variant="h5">{totalOrderPend}</Typography>
</TableCell>
<TableCell align="center">
<Typography variant="h5"> {totalOrderCancel}</Typography>
</TableCell>
<TableCell align="center">
<Typography variant="h5">{totalOrderComplete}</Typography>
</TableCell>
<TableCell align="center">
<Typography variant="h5">{totalDeliPend}</Typography>
</TableCell>
<TableCell align="center">
<Typography variant="h5"> {totalDeliCancel}</Typography>
</TableCell>
<TableCell align="center">
<Typography variant="h5">{totalDeliComplete}</Typography>
</TableCell>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell align="right" sx={{ pr: -2 }}>
<Typography variant="h5">{formatNumberToRupees(total)}</Typography>
</TableCell>
<TableCell></TableCell>
</TableRow>
)}
</TableBody>
</Table>
</TableContainer>
<Divider />
</MainCard>
{/* ============================================ || date filter dialog || ============================================ */}
<DateFilterDialog
open={open}
onClose={() => setOpen(false)}
onSelect={(range) => {
setStartdate(range.startDate);
setEnddate(range.endDate);
setDatestatus(range.label);
console.log('Selected Date Range:', range);
}}
/>
</>
);
}

View File

@@ -0,0 +1,296 @@
import React, { useState, useEffect, Fragment } from 'react';
import {
Box,
Drawer,
IconButton,
Toolbar,
Typography,
AppBar,
useMediaQuery,
Divider,
List,
ListItem,
ListItemText,
useTheme,
ListItemAvatar,
Stack,
Button,
Checkbox,
Skeleton
} from '@mui/material';
import MenuIcon from '@mui/icons-material/Menu';
import SearchBar from 'components/nearle_components/SearchBar';
import { useQuery } from '@tanstack/react-query';
import { fetchRidersLogs } from 'pages/api/api';
import RiderLocationMap from './RiderLocationMap';
import MainCard from 'components/MainCard';
import dayjs from 'dayjs';
import error500 from 'assets/images/maintenance/Error500.png';
const drawerWidth = 350;
const RidersLogs = () => {
const theme = useTheme();
const isDesktop = useMediaQuery('(min-width:900px)');
const [open, setOpen] = useState(false);
const [selectedRiders, setSelectedRiders] = useState([]);
const [riderSearch, setRiderSearch] = useState('');
const appId = 1;
const {
data: riders,
isLoading: ridersIsLoading,
isFetching: riderIsFetching,
refetch: riderLogsRefetch,
error: riderLogsError
} = useQuery({
queryKey: [appId, dayjs().format('YYYY-MM-DD'), riderSearch],
queryFn: fetchRidersLogs,
refetchInterval: 5 * 60 * 1000
});
useEffect(() => {
// const sortedRiders = riders?.sort((a, b) => a.firstname.localeCompare(b.firstname));
setSelectedRiders(riders);
}, [riders]);
useEffect(() => {
console.log('selectedRiders', selectedRiders);
}, [selectedRiders]);
useEffect(() => {
setOpen(isDesktop);
}, [isDesktop]);
return (
<MainCard content={false}>
<Box sx={{ display: 'flex', width: '100%', height: '100%', position: 'relative' }}>
{/* Drawer */}
<Drawer
variant={isDesktop ? 'persistent' : 'temporary'}
open={open}
onClose={() => !isDesktop && setOpen(false)}
ModalProps={{ keepMounted: true }}
sx={{
'& .MuiDrawer-paper': {
width: drawerWidth,
position: 'absolute',
left: 0,
top: 0,
height: '100%',
overflowY: 'auto',
transition: 'transform 0.35s ease-in-out',
zIndex: 13
}
}}
>
{/* Search */}
<Box sx={{ position: 'sticky', top: 0, zIndex: 1 }}>
<SearchBar
value={riderSearch}
placeholder="Search Rider"
onChange={(e) => setRiderSearch(e.target.value)}
sx={{
height: 60,
bgcolor: 'white',
'& .MuiOutlinedInput-notchedOutline': {
borderBottom: '1px solid',
borderColor: theme.palette.secondary.light
}
}}
/>
<List>
<ListItem sx={{ cursor: 'pointer', '&:hover': { bgcolor: theme.palette.secondary.lighter }, bgcolor: 'white', mt: -1 }}>
<ListItemAvatar>
<Checkbox
checked={riders?.length == selectedRiders?.length}
onChange={(e) => {
if (e.target.checked) {
setSelectedRiders(riders);
}
}}
/>
</ListItemAvatar>
<ListItemText primary="All" />
</ListItem>
<Divider />
</List>
</Box>
{/* Rider List */}
<List>
{/* Individuals */}
{ridersIsLoading || riderIsFetching
? Array.from({ length: 10 }).map((_, index) => (
<Fragment key={index}>
<ListItem sx={{ py: 1.5, px: 2 }}>
<ListItemAvatar>
<Skeleton variant="circular" width={24} height={24} />
</ListItemAvatar>
<ListItemText
primary={<Skeleton variant="text" width="60%" height={22} />}
secondary={<Skeleton variant="text" width="40%" height={18} />}
/>
<Stack spacing={0.5} textAlign="right">
<Skeleton variant="text" width={50} height={18} />
<Skeleton variant="text" width={80} height={16} />
</Stack>
</ListItem>
<Divider />
</Fragment>
))
: riders?.map((row) => {
return (
<Fragment key={row.userid}>
<ListItem
sx={{
cursor: 'pointer',
py: 1,
px: 2,
borderRadius: 1,
'&:hover': { bgcolor: theme.palette.secondary.lighter }
}}
secondaryAction={
<Stack textAlign="right" spacing={0.5}>
<Typography variant="body2" noWrap sx={{ color: row.status == 'active' ? 'success.main' : 'error.main' }}>
{row.userid}
</Typography>
<Typography variant="caption" color="text.secondary" noWrap>
{dayjs(row.logdate).format('DD/MM/YYYY hh:mm A')}
</Typography>
</Stack>
}
>
<ListItemAvatar>
<Checkbox
sx={{
color: row.status == 'active' ? 'green' : 'red',
'&.Mui-checked': {
color: row.status == 'active' ? 'green' : 'red'
}
}}
checked={
// INDIVIDUAL CHECKED CONDITION
selectedRiders?.length === 1 && selectedRiders[0]?.userid === row?.userid
}
onChange={(e) => {
if (e.target.checked) {
// SELECT ONE RIDER
setSelectedRiders([row]);
} else {
// UNCHECK -> SELECT ALL
setSelectedRiders(riders);
}
}}
/>
</ListItemAvatar>
<ListItemText
primary={
<Typography noWrap>
{row.username?.slice(0, 25) || ''}
{row.username?.length > 25 && '...'}
{/* {row.status === 'active' && <TaskAltIcon fontSize="small" color="success" sx={{ ml: 1 }} />} */}
</Typography>
}
secondary={
<Typography variant="caption" color="text.secondary" noWrap>
{row.contactno || '##########'}
</Typography>
}
/>
</ListItem>
<Divider />
</Fragment>
);
})}
</List>
</Drawer>
{/* AppBar */}
<AppBar
elevation={0}
position="absolute"
sx={{
top: 0,
left: open && isDesktop ? `${drawerWidth}px` : 0,
width: open && isDesktop ? `calc(100% - ${drawerWidth}px)` : '100%',
transition: 'left 0.3s ease, width 0.3s ease',
backgroundColor: 'white',
borderBottom: '1px solid',
borderColor: theme.palette.secondary.light
}}
>
<Toolbar>
<Stack direction="row" alignItems="center" justifyContent="space-between" sx={{ width: '100%' }}>
<Stack direction="row" alignItems="center">
<IconButton color="primary" onClick={() => setOpen(!open)}>
<MenuIcon />
</IconButton>
<Typography variant="h5" color="primary" sx={{ ml: 2 }}>
Riders Locations
</Typography>
</Stack>
<Button
variant="outlined"
color="primary"
onClick={() => {
riderLogsRefetch();
}}
>
Refresh
</Button>
</Stack>
</Toolbar>
</AppBar>
{/* Map */}
<Box
sx={{
flexGrow: 1,
overflow: 'auto',
pt: '64px',
pl: open && isDesktop ? `${drawerWidth}px` : 0,
transition: 'padding-left 0.3s ease',
minHeight: '80vh'
}}
>
{(ridersIsLoading || riderIsFetching) && (
<Box position="relative" width="100%" height="80vh" display="grid" placeItems="center">
{/* <CircularLoader /> */}
<Skeleton
variant="rectangular"
width="100%"
height="100%"
animation="wave"
sx={{
position: 'absolute',
top: 0,
left: 0,
borderRadius: 1,
zIndex: 1
}}
/>
</Box>
)}
{selectedRiders?.length > 0 && <RiderLocationMap riderLocations={selectedRiders} />}
{riderLogsError && (
<Box sx={{ width: '100% ', height: '100%' }}>
<img src={error500} alt="mantis" style={{ height: '100%', width: '100%' }} />
</Box>
)}
</Box>
</Box>
</MainCard>
);
};
export default RidersLogs;

View File

@@ -0,0 +1,454 @@
import { React, useState, useEffect } from 'react';
import TitleCard from 'pages/titleCard';
import axios from 'axios';
import { useQuery } from '@tanstack/react-query';
import { fetchRidersSummary } from 'pages/api/api';
import { Empty } from 'antd';
// material-ui
import {
Divider,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Typography,
Stack,
IconButton,
Tooltip,
Chip,
Collapse,
Dialog,
DialogContent
} from '@mui/material';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
import { IoLocationOutline } from 'react-icons/io5';
import dayjs from 'dayjs';
var utc = require('dayjs/plugin/utc');
dayjs.extend(utc);
import { CalendarMonth } from '@mui/icons-material';
import MainCard from 'components/MainCard';
import Loader from 'components/Loader';
import DateFilterDialog from 'components/DateFilterDialog';
import LocationAutocomplete from 'components/nearle_components/LocationAutocomplete';
import { OrdersTableSkeleton } from '../orders/OrdersTableSkeleton';
import RidersRoutes from './RidersRoutes';
import { OpenToast } from 'components/third-party/OpenToast';
function formatNumberToRupees(value) {
return new Intl.NumberFormat('en-IN', {
style: 'currency',
currency: 'INR',
minimumFractionDigits: 2
}).format(value);
}
const getColorByValue = (value) => {
return Number(value) !== 0 ? 'red' : 'inherit';
};
const coloredCell = (value) => <Typography sx={{ color: getColorByValue(value) }}>{value}</Typography>;
// ==============================|| MUI TABLE - ENHANCED ||============================== //
export default function RidersSummary() {
const [startdate, setStartdate] = useState(dayjs().format('YYYY-MM-DD'));
const [enddate, setEnddate] = useState(dayjs().format('YYYY-MM-DD'));
const [locaName, setLocoName] = useState('All');
const [open, setOpen] = useState(false);
const [datestatus, setDatestatus] = useState('Today');
const [total, settotal] = useState(0);
const [tenantData, setTenantData] = useState([]);
const [openRow, setOpenRow] = useState(null); // Initially no row is open
// const [appId, setAppId] = useState(localStorage.getItem('applocationid'));
const [appId, setAppId] = useState(0);
const [loading, setLoading] = useState(false);
const [mapOpen, setMapOpen] = useState(false);
const [logDetails, setLogDetails] = useState(null);
// ==============================|| fetchRidersSummary (riders summary)||============================== //
const { isLoading: isLoadingReports, data: rows } = useQuery({
queryKey: ['ridersummary', appId, startdate, enddate],
queryFn: fetchRidersSummary
});
// ==============================|| fetchTenantSummary by rider (rider summary)||============================== //
const fetchTenantSummary = async (riderUserid) => {
setLoading(true);
try {
const tenantRes = await axios.get(
`${process.env.REACT_APP_URL}/deliveries/getreportsummary/?&fromdate=${startdate}&todate=${enddate}&userid=${riderUserid}`
);
console.log('tenantRes', tenantRes.data.details);
setTenantData(tenantRes.data.details);
} catch (error) {
console.log('tenantRes', error);
} finally {
setLoading(false);
}
};
// ==============================|| getuserdeliverylogs (rider summary)||============================== //
const getuserdeliverylogs = async (userid) => {
try {
const response = await axios.get(
`${process.env.REACT_APP_URL}/deliveries/getuserdeliverylogs/?userid=${userid}&fromdate=2026-01-28&todate=2026-01-28 `
);
setLogDetails(response.data.details);
} catch (err) {
OpenToast(err?.message, 'error', 2000);
console.log('getuserdeliverylogs', err.message);
}
};
// ==============================|| calculate||============================== //
const calculate = async () => {
let calculatedTotal = 0;
rows &&
rows.forEach((row, index) => {
console.log(index, row.deliveryamt);
calculatedTotal += row.deliveryamt;
});
// Update the state after the calculation is done
settotal(calculatedTotal);
console.log('calculatedTotal', calculatedTotal);
};
useEffect(() => {
calculate();
}, [rows]);
return (
<>
{(isLoadingReports || loading) && (
<>
<Loader />
{/* <CircularLoader /> */}
</>
)}
<TitleCard title="Riders Summary" />
<MainCard
content={false}
title={
<Stack
display={'flex'}
flexDirection={'row'}
alignItems={'center'}
justifyContent={'space-between'}
flexWrap={'wrap'}
gap={1}
sx={{ border: '1px solid', borderColor: 'bg.main', p: 2 }}
>
<Stack direction="column" alignItems="flex-start" spacing={1} sx={{}}>
{startdate && enddate && (
<Stack direction="row" spacing={2} flexWrap={'wrap'} gap={1}>
<Chip label={`Orders-${datestatus}`} color="primary" variant="light" />
<Chip
label={
<Typography noWrap color="secondary">
{dayjs(startdate).format('DD/MM/YYYY')} - {dayjs(enddate).format('DD/MM/YYYY')}
</Typography>
}
variant="combined"
color="warning"
deleteIcon={<CalendarMonth style={{ fontSize: 18 }} />}
onDelete={() => {
setOpen(true);
}}
onClick={() => setOpen(true)}
sx={{ cursor: 'pointer' }}
/>
</Stack>
)}
{(!startdate || !enddate) && (
<>
<Stack direction="row" spacing={2}>
<Chip label="Orders-All" color="primary" variant="light" size="small" />
{/* <Chip label={<Typography noWrap color="secondary">ALL</Typography>} variant="combined" color='warning' size='small' /> */}
</Stack>
</>
)}
</Stack>
<Stack style={{ display: 'flex', flexDirection: 'row' }}>
<LocationAutocomplete
// ref={locationRef}
locaName={locaName}
setAppId={setAppId}
setLocoName={setLocoName}
// setPage={setPage}
sx={{ minWidth: 250, maxWidth: 1000, flex: 1 }}
/>
</Stack>
</Stack>
}
>
{/* table */}
<TableContainer>
<Table>
<TableHead>
<TableCell># </TableCell>
<TableCell>Rider </TableCell>
<TableCell> Orders</TableCell>
<TableCell> Pending</TableCell>
{/* <TableCell> Assigned</TableCell>
<TableCell>Accepted </TableCell>
<TableCell>Arrived </TableCell>
<TableCell>Picked </TableCell>
<TableCell>Active </TableCell>
<TableCell>Skipped </TableCell> */}
<TableCell>Cancelled </TableCell>
<TableCell>Delivered </TableCell>
<TableCell align="center">KMS </TableCell>
<TableCell align="center">Amount </TableCell>
<TableCell align="center">Action </TableCell>
</TableHead>
{/* ============================================ || TableBody || ============================================ */}
{isLoadingReports && <OrdersTableSkeleton col={10} />}
<TableBody>
{rows?.length === 0 && (
<TableRow>
<TableCell colSpan={15}>
<Stack width={'100%'} direction={'row'} justifyContent={'center'}>
<Empty />
</Stack>
</TableCell>
</TableRow>
)}
{rows?.length != 0 &&
rows?.map((row, index) => (
<>
{/* // ============================================ || tablerow 1 || ============================================ */}
<TableRow
key={row.tenantname}
sx={{
cursor: openRow === row.userid ? 'pointer' : null
}}
>
<TableCell align="left">{index + 1}</TableCell>
<TableCell>
<Stack spacing={1}>
<Typography sx={{ whiteSpace: 'noWrap' }}> {` ${row?.firstname} ${row?.lastname}`}</Typography>
<Typography variant="subtitle2">Id : {row.userid} </Typography>
</Stack>
</TableCell>
<TableCell>{coloredCell(row.totalorders)}</TableCell>
<TableCell>{coloredCell(row.pending)}</TableCell>
{/* <TableCell >{coloredCell(row.assigned)}</TableCell>
<TableCell >{coloredCell(row.accepted)}</TableCell>
<TableCell >{coloredCell(row.arrived)}</TableCell>
<TableCell >{coloredCell(row.picked)}</TableCell>
<TableCell >{coloredCell(row.active)}</TableCell>
<TableCell >{coloredCell(row.skipped)}</TableCell> */}
<TableCell>{coloredCell(row.cancelled)}</TableCell>
<TableCell>{coloredCell(row.delivered)}</TableCell>
<TableCell align="center">
<Stack direction="column" spacing={1}>
<Tooltip title="Kms" placement="top">
<Chip
size="small"
label={`${Number(row.kms).toFixed(2)}`}
color={row.kms ? 'error' : 'secondary'}
variant="combined"
sx={{
cursor: 'pointer',
minWidth: 80
}}
/>
</Tooltip>
<Tooltip title="Actual Kms" placement="bottom">
<Chip
size="small"
label={`${Number(row.cumulativekms).toFixed(2)}`}
color={row.cumulativekms ? 'success' : 'secondary'}
variant="combined"
sx={{
cursor: 'pointer',
minWidth: 80
}}
/>
</Tooltip>
</Stack>
</TableCell>
<TableCell align="center">
<Tooltip title="Total Amount" placement="top">
<Chip
size="small"
label={formatNumberToRupees(row.charges >= row.deliveryamt ? row.charges : row.deliveryamt)}
variant="combined"
color={row.deliveryamt ? 'primary' : 'secondary'}
sx={{
cursor: 'pointer',
minWidth: 100
}}
/>
</Tooltip>
</TableCell>
<TableCell align="center">
<Stack display={'flex'} flexDirection={'row'} gap={1} alignItems={'center'}>
<IconButton
aria-label="expand row"
size="small"
onClick={() => {
const isOpening = openRow !== row.userid;
// toggle row
setOpenRow(isOpening ? row.userid : null);
// ❌ closing → no API
if (!isOpening) return;
// ✅ opening → call API
fetchTenantSummary(row.userid);
}}
sx={{
bgcolor: openRow === row.userid ? 'primary.main' : null,
color: openRow === row.userid ? 'white' : null,
'&:hover': {
bgcolor: openRow === row.userid ? 'primary.main' : '#e1bee7'
}
}}
>
{openRow === row.userid ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
</IconButton>
<IconButton>
<IoLocationOutline
onClick={() => {
setMapOpen(true);
getuserdeliverylogs(row?.userid);
}}
/>
</IconButton>
</Stack>
</TableCell>
</TableRow>
{/* // ============================================ || collapsive row || ============================================ */}
{openRow === row.userid && (
<TableRow
sx={{
cursor: openRow === row.userid ? 'pointer' : null
}}
>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={15}>
<Collapse in={true} timeout="auto" unmountOnExit>
<MainCard content={false} sx={{ margin: 1 }}>
<Table size="small" aria-label="purchases">
<TableHead sx={{ bgcolor: 'primary.lighter' }}>
<TableRow>
<TableCell>#</TableCell>
<TableCell>Client</TableCell>
<TableCell align="center">All</TableCell>
<TableCell align="center">Pending</TableCell>
<TableCell align="center">Completed</TableCell>
<TableCell align="center">Cancelled</TableCell>
<TableCell align="center">Kms</TableCell>
<TableCell align="center">Amount</TableCell>
</TableRow>
</TableHead>
<TableBody>
{loading && <OrdersTableSkeleton col={3} />}
{tenantData?.map((row, index) => (
<TableRow key={row.tenantname}>
<TableCell align="left">{index + 1}</TableCell>
<TableCell align="left">
<Stack direction="row" sx={{ ml: -2 }}>
{row.tenantname}
</Stack>
</TableCell>
<TableCell align="center">{coloredCell(row.totalorders)}</TableCell>
<TableCell align="center">{coloredCell(row.deliveriespending)}</TableCell>
<TableCell align="center">{coloredCell(row.deliveriescompleted)}</TableCell>
<TableCell align="center">{coloredCell(row.deliveriescancelled)}</TableCell>
<TableCell align="center">
<Chip
size="small"
label={row.kms}
color="error"
variant="combined"
sx={{
mr: 1,
minWidth: 80
}}
/>
<Chip
size="small"
label={row.cumulativekms}
color="success"
variant="combined"
sx={{
minWidth: 80
}}
/>
</TableCell>
<TableCell align="center">
<Chip
size="small"
label={formatNumberToRupees(row.charges >= row.deliveryamt ? row.charges : row.deliveryamt)}
sx={{
color: 'primary.main',
bgcolor: '#e1bee7',
border: '1px solid #662582 ',
minWidth: 100
}}
/>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</MainCard>
</Collapse>
</TableCell>
</TableRow>
)}
</>
))}
</TableBody>
</Table>
</TableContainer>
<Divider />
{rows?.length != 0 && (
<Stack direction={'row'} sx={{ display: 'flex', justifyContent: 'end', px: 1, py: 2 }}>
<Typography variant="h5">Total :</Typography>
<Typography variant="h5" sx={{ ml: 5, mr: 1.5 }}>
{formatNumberToRupees(total)}
</Typography>
</Stack>
)}
</MainCard>
<Dialog
maxWidth={'xl'}
fullScreen
open={mapOpen}
onClose={() => {
setMapOpen(false);
}}
>
<DialogContent>{logDetails && <RidersRoutes details={logDetails} />}</DialogContent>
</Dialog>
{/* ============================================= || filter Dialog | ============================================= */}
<DateFilterDialog
open={open}
onClose={() => setOpen(false)}
onSelect={(range) => {
setStartdate(range.startDate);
setEnddate(range.endDate);
setDatestatus(range.label);
console.log('Selected Date Range:', range);
}}
/>
</>
);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,452 @@
import { useEffect, useState } from 'react';
// material-ui
import { Button, Grid, InputLabel, MenuItem, Select, Stack, TextField, Typography } from '@mui/material';
// third-party
// import { PatternFormat } from 'react-number-format';
// project import
import MainCard from 'components/MainCard';
import axios from 'axios';
// assets
import { usePlacesWidget } from 'react-google-autocomplete';
import Loader from 'components/Loader';
import Geocode from 'react-geocode';
import { enqueueSnackbar } from 'notistack';
import { useNavigate } from 'react-router';
// import { setLocationType } from 'react-geocode';
// const avatarImage = require.context('assets/images/users', true);
// styles & constant
// const ITEM_HEIGHT = 48;
// const ITEM_PADDING_TOP = 8;
// const MenuProps = {
// PaperProps: {
// style: {
// maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP
// }
// }
// };
const Createrider = () => {
// const [role, setRole] = useState('');
const [mobilenumber, setMobilenumber] = useState('');
const [emailaddress, setEmailaddress] = useState('');
const [city, setCity] = useState('');
const [zipcode, setZipcode] = useState('');
const [address, setAddress] = useState('');
const [state, setState] = useState('');
const [suburb, setSuburb] = useState('');
const [latlong, setLatlong] = useState({});
const [firstname, setFirstname] = useState('');
const [doorno, setDoorno] = useState('');
const [landmark, setLandmark] = useState('');
const [tenantinfo, setTenantinfo] = useState({});
const navigate = useNavigate();
Geocode.setApiKey(process.env.REACT_APP_GOOGLE_MAPS_API_KEY);
// Geocode.setApiKey('AIzaSyCF4KatYCI3vqz1_H3kiHeyS3yCMfYToh8');
const [loading, setLoading] = useState(false);
useEffect(() => {
// fetchprofiledetails(localStorage.getItem('appuserid'));
// fetchprofiledetails(181);
if (localStorage.getItem('tenantid')) {
fetchtenantinfo(localStorage.getItem('tenantid'));
}
}, []);
useEffect(() => {
try {
Geocode.fromAddress(address).then(
(response) => {
if (response.status == 'OK') {
const { lat, lng } = response.results[0].geometry.location;
setLatlong({
lat,
lng
});
console.log(response);
}
},
(error) => {
console.log(error);
}
);
} catch (err) {
console.log(err);
}
}, [address]);
const opentoast = (message) => {
enqueueSnackbar(message, {
variant: 'error',
anchorOrigin: { vertical: 'top', horizontal: 'right' },
autoHideDuration: 2000
});
// console.log(alertmessage)
};
const fetchtenantinfo = async (tid) => {
setLoading(true);
await axios
.get(`${process.env.REACT_APP_URL}/tenants/gettenantinfo/?tenantid=${tid}`)
.then((res) => {
console.log(res);
if (res.data.status) {
setTenantinfo(res.data.details);
}
setLoading(false);
})
.catch((err) => {
console.log(err);
setLoading(false);
});
};
useEffect(() => {
if (selectedImage) {
setAvatar(URL.createObjectURL(selectedImage));
}
}, [selectedImage]);
const { ref: materialRef } = usePlacesWidget({
apiKey: process.env.REACT_APP_GOOGLE_MAPS_API_KEY,
onPlaceSelected: (place) => {
console.log(place);
setAddress(place.formatted_address);
let city1, zipcode1, state1, suburb1;
for (let i = 0; i < place.address_components.length; i++) {
for (let j = 0; j < place.address_components[i].types.length; j++) {
switch (place.address_components[i].types[j]) {
case 'locality':
city1 = place.address_components[i].long_name;
break;
case 'administrative_area_level_1':
state1 = place.address_components[i].long_name;
break;
case 'postal_code':
zipcode1 = place.address_components[i].long_name;
break;
case 'sublocality':
suburb1 = place.address_components[i].long_name;
break;
}
}
}
setCity(city1 || '');
setState(state1 || '');
setZipcode(zipcode1 || '');
setSuburb(suburb1 || '');
// setAddress(place.formatted_address)
},
// inputAutocompleteValue: "country",
options: {
// componentRestrictions: 'us',
// types: ["establishment"]
types: ['address' || 'geocode']
}
});
const createprofile = async () => {
console.log('res', businessname, businessno, mobilenumber, emailaddress, address, city, zipcode);
// if (!businessname) {
// opentoast('Fill Business name')
// } else if (!businessno) {
// opentoast('Fill Registration No')
// }
// else
if (!firstname) {
opentoast('Fill Full name');
} else if (!mobilenumber) {
opentoast('Fill Mobile Number');
} else if (!emailaddress) {
opentoast('Fill emailaddress');
} else if (!address) {
opentoast('Fill Address');
} else if (!city) {
opentoast('Fill City');
} else if (!zipcode) {
opentoast('Fill post code');
} else if (!suburb) {
opentoast('Fill suburb');
} else if (!latlong.lat || !latlong.lng) {
opentoast('Choose valid address');
} else {
let obj = {
customerid: 0,
configid: 1,
firstname: firstname,
applocationid: tenantinfo.applolcationid,
profileimage: '',
dialcode: '+91',
contactno: mobilenumber,
devicetype: '',
deviceid: '',
customertoken: '',
address: address,
suburb: suburb,
city: city,
state: state,
postcode: zipcode,
landmark: landmark,
doorno: doorno,
latitude: latlong.lat.toString(),
longitude: latlong.lng.toString(),
tenantid: parseInt(localStorage.getItem('tenantid')),
email: emailaddress
};
console.log(obj);
setLoading(true);
try {
await axios
.post(`${process.env.REACT_APP_URL}/customers/create`, obj)
.then((res) => {
console.log(res);
if (res.data.status) {
enqueueSnackbar(' Created Successfully ', {
variant: 'success',
anchorOrigin: { vertical: 'top', horizontal: 'right' },
autoHideDuration: 2000
});
navigate('/clients');
// setTimeout(()=>{
// fetchprofiledetails(localStorage.getItem('appuserid'));
// },2000)
} else if (res.data.message == 'Customer Already available') {
enqueueSnackbar('Customer Already available', {
variant: 'error',
anchorOrigin: { vertical: 'top', horizontal: 'right' },
autoHideDuration: 2000
});
}
setLoading(false);
})
.catch((err) => {
console.log(err);
setLoading(false);
enqueueSnackbar(err.message, {
variant: 'error',
anchorOrigin: { vertical: 'top', horizontal: 'right' },
autoHideDuration: 2000
});
});
} catch (err) {
console.log(err);
setLoading(false);
}
}
};
return (
<>
{loading && <Loader />}
<Grid item xs={12} sx={{ mb: 2 }}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Typography variant="h3">Create Rider</Typography>
</Stack>
</Grid>
<MainCard>
<Grid container spacing={3}>
<Grid item xs={12}>
<MainCard
// title="Contact Information"
sx={{ height: '100%' }}
>
<Grid container spacing={3}>
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-last-name">Admin Name</InputLabel>
<TextField
fullWidth
id="personal-last-name"
placeholder="Name"
onChange={(e) => setFirstname(e.target.value)}
value={firstname}
autoComplete="off"
/>
</Stack>
</Grid>
<Grid item xs={12} sm={6}></Grid>
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-phone">Phone Number</InputLabel>
<Stack direction="row" justifyContent="space-between" alignItems="center" spacing={2}>
<Select defaultValue="+1" disabled sx={{ cursor: 'not-allowed' }}>
<MenuItem value="+1">+91</MenuItem>
</Select>
<TextField
type="number"
id="personal-phone"
// format="##########"
// mask="_"
fullWidth
// customInput={TextField}
placeholder="Phone Number"
// defaultValue="8654239581"
// onBlur={() => { }}
onChange={(e) => {
if (e.target.value.toString().length <= 10) {
setMobilenumber(e.target.value);
}
}}
value={mobilenumber}
autoComplete="off"
// disabled
sx={{ cursor: 'not-allowed' }}
/>
</Stack>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-email">Email Address</InputLabel>
<TextField
type="email"
fullWidth
// defaultValue="stebin.ben@gmail.com"
id="personal-email"
placeholder="Email Address"
onChange={(e) => setEmailaddress(e.target.value)}
value={emailaddress}
autoComplete="off"
/>
</Stack>
</Grid>
<Grid item xs={12}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-address">Address</InputLabel>
<TextField
fullWidth
// defaultValue="Street 110-B Kalians Bag, Dewan, M.P. New York"
id="personal-address"
placeholder="Address"
value={address}
onChange={(e) => setAddress(e.target.value)}
inputRef={materialRef}
/>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-location">Suburb</InputLabel>
<TextField
fullWidth
// defaultValue="New York"
id="personal-location"
placeholder="Location"
onChange={(e) => setSuburb(e.target.value)}
value={suburb}
autoComplete="off"
/>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-zipcode">City</InputLabel>
<TextField
fullWidth
// defaultValue="956754"
// type='number'
id="personal-zipcode"
placeholder="City"
onChange={(e) => setCity(e.target.value)}
value={city}
autoComplete="off"
/>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-location">State</InputLabel>
<TextField
fullWidth
// defaultValue="New York"
id="personal-location"
placeholder="State"
onChange={(e) => setState(e.target.value)}
value={state}
autoComplete="off"
/>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-zipcode">Post Code</InputLabel>
<TextField
fullWidth
// defaultValue="956754"
type="number"
id="personal-zipcode"
placeholder="Zipcode"
onChange={(e) => setZipcode(e.target.value)}
value={zipcode}
autoComplete="off"
/>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-location">Door No</InputLabel>
<TextField
fullWidth
// defaultValue="New York"
id="personal-location"
placeholder="Door No"
onChange={(e) => setDoorno(e.target.value)}
value={doorno}
autoComplete="off"
/>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="personal-email">Landmark</InputLabel>
<TextField
type="email"
fullWidth
// defaultValue="stebin.ben@gmail.com"
id="personal-email"
placeholder="Landmark"
onChange={(e) => setLandmark(e.target.value)}
value={landmark}
autoComplete="off"
/>
</Stack>
</Grid>
</Grid>
</MainCard>
</Grid>
<Grid item xs={12}>
<Stack direction="row" justifyContent="flex-end" alignItems="center" spacing={2}>
<Button variant="contained" onClick={() => createprofile()}>
Create
</Button>
</Stack>
</Grid>
</Grid>
</MainCard>
</>
);
};
export default Createrider;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,501 @@
import * as React from 'react';
import { useState, useEffect, useRef } from 'react';
import Geocode from 'react-geocode';
import { Empty } from 'antd';
import { useNavigate } from 'react-router-dom';
import { FaRegEdit } from 'react-icons/fa';
import {
Avatar,
Stack,
Chip,
Typography,
Table,
TableCell,
TableBody,
TableHead,
IconButton,
Tabs,
Tab,
TableRow,
Tooltip,
TableContainer,
Divider,
Backdrop,
Collapse
} from '@mui/material';
var utc = require('dayjs/plugin/utc');
import { useTheme } from '@mui/material/styles';
import { DownOutlined, UpOutlined } from '@ant-design/icons';
import dayjs from 'dayjs';
dayjs.extend(utc);
import MainCard from 'components/MainCard';
import TitleCard from 'components/nearle_components/TitleCard';
import LocationAutocomplete from 'components/nearle_components/LocationAutocomplete';
import DebounceSearchBar from 'components/nearle_components/DebounceSearchBar';
import CircularLoader from 'components/CircularLoader';
import { fetchAllRiders, getallridersummary, getriderstatus } from 'pages/api/api';
import { useInfiniteQuery, useQuery } from '@tanstack/react-query';
import LoaderWithImage from 'components/nearle_components/LoaderWithImage';
import { OrdersTableSkeleton } from '../orders/OrdersTableSkeleton';
import { OpenToast } from 'components/third-party/OpenToast';
import axios from 'axios';
import LocationOnIcon from '@mui/icons-material/LocationOn';
import BatteryStdIcon from '@mui/icons-material/BatteryStd';
import SpeedIcon from '@mui/icons-material/Speed';
import AccessTimeIcon from '@mui/icons-material/AccessTime';
import PowerIcon from '@mui/icons-material/Power';
import GpsFixedIcon from '@mui/icons-material/GpsFixed';
const Riders = () => {
const theme = useTheme();
const navigate = useNavigate();
const loadMoreRef = useRef();
const containerRef = useRef();
const [searchword, setSearchword] = useState('');
const [debouncedSearch, setDebouncedSearch] = useState('');
const [locaName, setLocoName] = useState('All');
const [appId, setAppId] = useState(0);
const [tabvalue, setTabvalue] = useState(0);
const roleid = localStorage.getItem('roleid');
const [logsRow, setLogsRow] = useState(null);
const [riderLogsdata, setRiderLogsdata] = useState(null);
Geocode.setApiKey(process.env.REACT_APP_GOOGLE_MAPS_API_KEY);
const handleChangetab = (e, i) => {
setTabvalue(i);
setLogsRow(null);
};
// ==============================|| getallridersummary||============================== //
const { data: allRidersSummary, isLoading: riderSummarysLoading } = useQuery({
queryKey: ['allriders', appId, tabvalue],
queryFn: getallridersummary
});
// ==============================|| getRiderLogs (riders)||============================== //
const getRiderLogs = async (userid) => {
try {
const res = await axios.get(`${process.env.REACT_APP_URL}/utils/getriderperiodiclogs?userid=${userid}`);
if (res.data.data.length == 0) {
setLogsRow(null);
OpenToast(res.data.message, 'error', 2000);
} else {
setRiderLogsdata(res.data.data);
}
} catch (err) {
OpenToast(err.message, 'error', 2000);
}
};
// ==============================|| getriderstatus||============================== //
const {
data: ridersStatus,
isLoading: riderStatusLoading,
isError: riderstatusIsError,
error: riderStatusError
} = useQuery({
queryKey: ['ridersStatus'],
queryFn: getriderstatus
});
useEffect(() => {
if (ridersStatus) {
console.log('Success:', ridersStatus);
}
}, [ridersStatus]);
// ==============================|| fetchAllRiders||============================== //
const {
data: allRidersData,
isLoading: allRidersLoading,
isFetchingNextPage,
fetchNextPage,
hasNextPage
} = useInfiniteQuery({
queryKey: ['allriders', appId, debouncedSearch, tabvalue],
queryFn: fetchAllRiders,
getNextPageParam: (lastPage, pages) => (lastPage.details?.length ? pages.length + 1 : undefined)
});
const rows = allRidersData?.pages.flatMap((page) => page.details) || [];
useEffect(() => {
if (!hasNextPage) return;
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting) {
fetchNextPage();
}
},
{
root: document.querySelector('.MuiTableContainer-root'), // 👈 or explicitly TableContainer
rootMargin: '0px',
threshold: 1.0
}
);
if (loadMoreRef.current) observer.observe(loadMoreRef.current);
return () => {
if (loadMoreRef.current) observer.unobserve(loadMoreRef.current);
};
}, [hasNextPage, fetchNextPage]);
const handleScroll = (event) => {
const { scrollTop, scrollHeight, clientHeight } = event.currentTarget;
if (scrollTop + clientHeight >= scrollHeight - 50) {
if (hasNextPage && !isFetchingNextPage) {
fetchNextPage();
}
}
};
const errMessage = riderstatusIsError ? riderStatusError : null;
useEffect(() => {
if (errMessage) {
OpenToast(errMessage, 'error', 2000);
}
}, [errMessage]);
return (
<>
{
<Backdrop
sx={{
color: '#fff',
zIndex: (theme) => theme.zIndex.drawer + 1
}}
open={allRidersLoading || riderSummarysLoading || riderStatusLoading} // when loader = true, backdrop covers the page
>
<CircularLoader color="inherit" />
</Backdrop>
}
{/* ============================================= || titlecard | ============================================= */}
<TitleCard title="Riders">
<LocationAutocomplete
locaName={locaName}
setAppId={setAppId}
setLocoName={setLocoName}
sx={{ width: { xs: '100%', custom450: 300 }, zIndex: '100' }}
/>
</TitleCard>
<Stack
minWidth={'100%'}
flexDirection="row"
sx={{
p: 1.5,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
flexWrap: 'wrap-reverse',
gap: 1,
border: '1px solid',
borderColor: 'bg.main',
mt: 2
}}
>
<Tabs value={tabvalue} onChange={handleChangetab} variant="scrollable" scrollButtons="auto">
<Tab
label="ALL"
iconPosition="end"
icon={searchword ? null : <Chip label={allRidersSummary?.total} color="primary" variant="light" size="small" />}
/>
<Tab
label="Active"
iconPosition="end"
icon={searchword ? null : <Chip label={allRidersSummary?.active} color="primary" variant="light" size="small" />}
/>
</Tabs>
<DebounceSearchBar
value={searchword}
onChange={setSearchword}
onDebouncedChange={setDebouncedSearch}
sx={{ width: { xs: '100%', custom600: 275 }, m: 0 }}
/>
</Stack>
<MainCard content={false}>
<TableContainer
ref={containerRef}
onScroll={handleScroll}
sx={{
maxHeight: 'calc(100vh - 185px)',
overflow: 'auto',
'&::-webkit-scrollbar': {
width: '12px', // scroll bar width
cursor: 'pointer'
},
'&::-webkit-scrollbar-thumb': {
backgroundColor: theme.palette.primary.main, // thumb color
borderRadius: '8px',
cursor: 'pointer'
},
'&::-webkit-scrollbar-thumb:hover': {
backgroundColor: theme.palette.primary.dark, // hover color
cursor: 'pointer'
},
'&::-webkit-scrollbar-track': {
backgroundColor: theme.palette.primary.lighter,
cursor: 'pointer'
}
}}
>
<Table stickyHeader>
<TableHead>
<TableRow>
<TableCell sx={{ position: 'sticky !important' }}>S.NO</TableCell>
<TableCell sx={{ position: 'sticky !important', whiteSpace: 'nowrap', minWidth: 100 }}>User ID</TableCell>
<TableCell sx={{ position: 'sticky !important' }}> Rider </TableCell>
<TableCell sx={{ position: 'sticky !important' }}> Address </TableCell>
<TableCell sx={{ position: 'sticky !important' }}> Vehicle </TableCell>
<TableCell sx={{ position: 'sticky !important' }}> Shift </TableCell>
<TableCell align="center" sx={{ position: 'sticky !important' }}>
Time
</TableCell>
<TableCell sx={{ position: 'sticky !important' }}> Fare </TableCell>
<TableCell sx={{ position: 'sticky !important' }}> Fuel </TableCell>
<TableCell align="center" sx={{ position: 'sticky !important' }}>
Status
</TableCell>
{roleid == 1 && (
<TableCell align="center" sx={{ position: 'sticky !important' }}>
Action{' '}
</TableCell>
)}
</TableRow>
</TableHead>
<Divider />
<TableBody>
{allRidersLoading && <OrdersTableSkeleton col={6} />}
{rows?.length == 0 && !allRidersLoading && (
<>
<TableCell colSpan={11}>
<Stack width={'100%'} direction={'row'} justifyContent={'center'}>
<Empty />
</Stack>
</TableCell>
</>
)}
{rows?.length != 0 &&
rows?.map((row, index) => {
return (
<>
<TableRow key={index + 1} sx={{ cursor: 'pointer' }}>
<TableCell>{index + 1}</TableCell>
<TableCell>
<Chip label={row?.userid} color="primary" variant="light" />
</TableCell>
<TableCell align="left" sx={{ paddingLeft: '0px !important' }}>
<Stack direction="row" alignItems="center" gap={2} justifyContent="flex-start">
<Avatar
sx={{
width: 35,
height: 35,
fontSize: 20
}}
>
{row.fullname?.charAt(0).toUpperCase()}
</Avatar>
<Stack direction="column" gap={1}>
<Typography>{`${row.username}`}</Typography>
<Typography variant="caption">{row.contactno}</Typography>
</Stack>
</Stack>
</TableCell>
<TableCell align="left">
<Tooltip title={row.address}>
<Stack direction="column">
<Typography variant="caption">{row.suburb || row.address.slice(0, 20)}</Typography>
<Typography>{row.city}</Typography>
</Stack>
</Tooltip>
</TableCell>
<TableCell align="left">{row.vehicleno}</TableCell>
<TableCell>{row.shiftid}</TableCell>
<TableCell align="center">
<Stack display={'flex'} flexDirection={'column'} gap={1}>
<Chip
size="small"
color="success"
variant="light"
label={dayjs(`${dayjs().format('MM-DD-YYYY')} ${row.starttime}`).format('hh:mm A')}
sx={{ width: 100 }}
/>
<Chip
size="small"
color="error"
variant="light"
label={dayjs(`${dayjs().format('MM-DD-YYYY')} ${row.endtime}`).format('hh:mm A')}
sx={{ width: 100 }}
/>
</Stack>
</TableCell>
<TableCell align="left">{row.basefare}</TableCell>
<TableCell align="left">{row.fuelcharge}</TableCell>
{tabvalue == 0 ? (
<TableCell>
{row.status == 'Active' && (
<Chip label="Active" color="success" size="small" sx={{ width: 80 }} variant="light" />
)}
{row.status == 'InActive' && (
<Chip label="In Active" color="error" size="small" sx={{ width: 80 }} variant="light" />
)}
</TableCell>
) : (
<TableCell>
{(() => {
const state = ridersStatus?.find((status) => status.userid === row.userid);
const statusText = state?.status;
console.log('statusText', state);
return (
<Chip
label={statusText || 'Unknown'}
color={
statusText === 'Active'
? 'primary'
: statusText === 'Offline'
? 'error'
: statusText === 'Online'
? 'success'
: 'default'
}
size="small"
sx={{ width: 80 }}
variant="light"
/>
);
})()}
</TableCell>
)}
{roleid == 1 && (
<TableCell align="center">
<Stack direction={'row'}>
<Tooltip title="Edit Rider">
<IconButton
onClick={() => {
navigate('/nearle/riders/edit', { state: { riderdata: row } });
}}
>
<FaRegEdit />
</IconButton>
</Tooltip>
{tabvalue != 0 && (
<IconButton
onClick={() => {
if (row.userid == logsRow) {
setLogsRow(null);
} else {
setLogsRow(row.userid);
getRiderLogs(row.userid);
}
}}
>
{row.userid == logsRow ? <UpOutlined /> : <DownOutlined />}
</IconButton>
)}
</Stack>
</TableCell>
)}
</TableRow>
{logsRow === row.userid && tabvalue !== 0 && (
<TableRow>
<TableCell colSpan={11} sx={{ p: 0 }}>
<Collapse in={logsRow === row.userid} timeout="auto" unmountOnExit>
<MainCard content={false}>
<Table size="small">
{/* Header */}
<TableHead>
<TableRow>
<TableCell>Location</TableCell>
<TableCell>Battery</TableCell>
<TableCell>Charging</TableCell>
<TableCell>Speed</TableCell>
<TableCell>Accuracy</TableCell>
<TableCell>Time</TableCell>
<TableCell>Order</TableCell>
<TableCell>Status</TableCell>
</TableRow>
</TableHead>
{/* Body */}
<TableBody>
<TableRow>
<TableCell>
<Stack direction="row" spacing={1}>
<LocationOnIcon color="error" fontSize="small" />
<Typography variant="body2">
{riderLogsdata?.latitude}, {riderLogsdata?.longitude}
</Typography>
</Stack>
</TableCell>
<TableCell>
<Stack direction="row" spacing={1}>
<BatteryStdIcon fontSize="small" />
<Typography>{riderLogsdata?.battery || 'N/A'}</Typography>
</Stack>
</TableCell>
<TableCell>
<Stack direction="row" spacing={1}>
<PowerIcon color={riderLogsdata?.is_charging ? 'success' : 'disabled'} fontSize="small" />
<Typography>{riderLogsdata?.is_charging ? 'Charging' : 'Not Charging'}</Typography>
</Stack>
</TableCell>
<TableCell>
<Stack direction="row" spacing={1}>
<SpeedIcon fontSize="small" />
<Typography>{riderLogsdata?.speed} km/h</Typography>
</Stack>
</TableCell>
<TableCell>
<Stack direction="row" spacing={1}>
<GpsFixedIcon fontSize="small" />
<Typography>{riderLogsdata?.accuracy} m</Typography>
</Stack>
</TableCell>
<TableCell>
<Stack direction="row" spacing={1}>
<AccessTimeIcon fontSize="small" />
<Typography>{riderLogsdata?.logdate}</Typography>
</Stack>
</TableCell>
<TableCell>{riderLogsdata?.orderid || 'N/A'}</TableCell>
<TableCell>
<Chip
label={riderLogsdata?.status || 'unknown'}
color={riderLogsdata?.status === 'idle' ? 'warning' : 'success'}
size="small"
/>
</TableCell>
</TableRow>
</TableBody>
</Table>
</MainCard>
</Collapse>
</TableCell>
</TableRow>
)}
</>
);
})}
{rows?.length != 0 && (
<TableRow>
<TableCell colSpan={15} rowSpan={3}>
<div ref={loadMoreRef} style={{ height: 40, textAlign: 'center' }}>
{isFetchingNextPage ? <LoaderWithImage /> : hasNextPage ? <LoaderWithImage /> : 'No More Riders'}
</div>
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</TableContainer>
</MainCard>
</>
);
};
export default Riders;

View File

@@ -0,0 +1,173 @@
import { Grid, TextField, Typography } from '@mui/material';
import { useQuery } from '@tanstack/react-query';
import CircularLoader from 'components/CircularLoader';
import Loader from 'components/Loader';
import MainCard from 'components/MainCard';
import { getusers } from 'pages/api/api';
const ViewProfile = () => {
const {
data: userData,
isLoading
} = useQuery({
queryKey: ['getuser'],
queryFn: getusers
});
return (
<>
{isLoading && (
<>
<Loader />
<CircularLoader />
</>
)}
<MainCard
title={
<Typography variant="h3" sx={{ m: 3 }}>
User Profile
</Typography>
}
>
<Grid container spacing={4}>
{/* ==============================|| userid ||============================== */}
<Grid item xs={12} sm={6} md={4}>
<TextField
fullWidth
label={'User ID'}
value={userData?.userid || ''}
InputProps={{
readOnly: true
}}
variant="standard"
/>
</Grid>
{/* ==============================|| Name ||============================== */}
<Grid item xs={12} sm={6} md={4}>
<TextField
fullWidth
label={'User Name'}
value={userData?.fullname || ''}
InputProps={{
readOnly: true
}}
variant="standard"
/>
</Grid>
{/* ==============================|| Location ||============================== */}
<Grid item xs={12} sm={6} md={4}>
<TextField
fullWidth
label={'App Location '}
value={userData?.applocation || ''}
InputProps={{
readOnly: true
}}
variant="standard"
/>
</Grid>
{/* ==============================|| Authname ||============================== */}
<Grid item xs={12} sm={6} md={4}>
<TextField
fullWidth
label={'Auth Name'}
value={userData?.authname || ''}
InputProps={{
readOnly: true
}}
variant="standard"
/>
</Grid>
{/* ==============================|| contactno ||============================== */}
<Grid item xs={12} sm={6} md={4}>
<TextField
fullWidth
label={'Contact No'}
value={userData?.contactno || ''}
InputProps={{
readOnly: true
}}
variant="standard"
/>
</Grid>
{/* ==============================|| email ||============================== */}
<Grid item xs={12} sm={6} md={4}>
<TextField
fullWidth
label={'E-Mail'}
value={userData?.email || ''}
InputProps={{
readOnly: true
}}
variant="standard"
/>
</Grid>
{/* ==============================|| address ||============================== */}
<Grid item xs={12}>
<TextField
fullWidth
label={'Address'}
value={userData?.address || ''}
InputProps={{
readOnly: true
}}
variant="standard"
/>
</Grid>
{/* ==============================|| Location ||============================== */}
<Grid item xs={12} sm={6} md={4}>
<TextField
fullWidth
label={'Location'}
value={userData?.suburb || ''}
InputProps={{
readOnly: true
}}
variant="standard"
/>
</Grid>
{/* ==============================|| city ||============================== */}
<Grid item xs={12} sm={6} md={4}>
<TextField
fullWidth
label={'City'}
value={userData?.city || ''}
InputProps={{
readOnly: true
}}
variant="standard"
/>
</Grid>
{/* ==============================|| state ||============================== */}
<Grid item xs={12} sm={6} md={4}>
<TextField
fullWidth
label={'State'}
value={userData?.state || ''}
InputProps={{
readOnly: true
}}
variant="standard"
/>
</Grid>
{/* ==============================|| Postcode ||============================== */}
<Grid item xs={12} sm={6} md={4}>
<TextField
fullWidth
label={'Postcode'}
value={userData?.postcode || ''}
InputProps={{
readOnly: true
}}
variant="standard"
/>
</Grid>
</Grid>
</MainCard>
</>
);
};
export default ViewProfile;

31
src/pages/titleCard.js Normal file
View File

@@ -0,0 +1,31 @@
import React from 'react';
import { useTheme } from '@mui/material/styles';
import { CardActions, Grid, Stack, Typography } from '@mui/material';
const TitleCard = ({ title, secondary, sx }) => {
const theme = useTheme();
return (
<CardActions
sx={{
position: 'sticky',
top: '30px',
bgcolor: theme.palette.background.default,
zIndex: 10,
width: '100%',
...sx
}}
>
<Grid container>
<Grid item xs={12}>
<Stack direction="row" justifyContent="space-between" alignItems="center" gap={2} sx={{ flexWrap: 'wrap' }}>
<Typography variant="h3">{title}</Typography>
{secondary}
</Stack>
</Grid>
</Grid>
</CardActions>
);
};
export default TitleCard;