Files
doormile_console_expresscopy/src/pages/nearle/riders/editRider.js
Suriya c52350df0f feat: convert NearlExpress console to Doormile admin
Phase 1: env config + login auth (Doormile JWT)
Phase 2: full API layer rewrite in api.js
  - All endpoints now point at api.doormile.com/api/v1
  - Removed all jupiter.nearle.app references
  - Removed all routes.workolik.com references (Phase 3)
  - Miler CRUD: GET/POST/PATCH /admin/milers
  - Clients: GET/POST/PATCH /crm/clients
  - Bookings: GET /admin/bookings
  - Auto-assign: POST /hub/bookings/:id/auto-assign

Pages rewritten:
  - riders.js - Doormile miler fields, status mapping
  - createrider.js - fixed broken form, real miler creation
  - editRider.js - stripped bank/vehicle/insurance, hub mapping
  - Tenants.js - stripped pricing dialog, CRM client fields

Menu: renamed Orders/Deliveries/Tenants/Riders/Dispatch
  to Bookings/Consignments/Clients/Milers/Live Operations

Pending Phase 3: dispatch pages, reports, consignments
2026-07-08 15:47:01 +05:30

214 lines
7.6 KiB
JavaScript

import { useEffect, useState } from 'react';
// material-ui
import { useLocation, useNavigate } from 'react-router-dom';
import { Box, Button, Grid, InputLabel, MenuItem, Select, Stack, TextField, Typography, useMediaQuery, useTheme } from '@mui/material';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
// project import
import MainCard from 'components/MainCard';
import axios from 'axios';
import Loader from 'components/Loader';
import { enqueueSnackbar } from 'notistack';
import CircularLoader from 'components/CircularLoader';
import { fetchAppLocations } from 'pages/api/api';
const AVAILABILITY_OPTIONS = ['Available', 'Assigned', 'On_Break', 'Offline'];
const EditRider = () => {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
const location = useLocation();
const [riderdata, setRiderdata] = useState(null);
const navigate = useNavigate();
const [hubs, setHubs] = useState([]);
const [loading, setLoading] = useState(false);
const fetchRiderData = async (id) => {
try {
// Doormile /admin/milers/:id returns { userid, displayname, phone,
// availabilitystatus, rating, hubid, currentlat, currentlon,
// completedorders, cancelledorders } — this form only edits the
// fields Doormile's PATCH endpoint actually accepts.
const riderdataresponse = await axios.get(`${process.env.REACT_APP_URL}/admin/milers/${id}`);
setRiderdata(riderdataresponse.data.data);
} catch (error) {
console.log('fetchRiderData', error);
}
};
useEffect(() => {
fetchRiderData(location.state.riderdata.userid);
fetchAppLocations().then((locations) => {
setHubs((locations || []).filter((l) => l.applocationid !== 0));
});
}, []);
const updateRider = async () => {
setLoading(true);
try {
const response = await axios.patch(`${process.env.REACT_APP_URL}/admin/milers/${riderdata.userid}`, {
displayname: riderdata.displayname,
phone: riderdata.phone,
availabilitystatus: riderdata.availabilitystatus,
hubid: riderdata.hubid
});
if (response.status == 200) {
enqueueSnackbar('Updated Successfully', {
variant: 'success',
anchorOrigin: { vertical: 'top', horizontal: 'right' },
autoHideDuration: 2000
});
navigate('/nearle/riders');
} else {
enqueueSnackbar('Update Failed', {
variant: 'error',
anchorOrigin: { vertical: 'top', horizontal: 'right' },
autoHideDuration: 2000
});
}
} catch (err) {
enqueueSnackbar(err.response?.data?.message || err.message || 'Update Failed', {
variant: 'error',
anchorOrigin: { vertical: 'top', horizontal: 'right' },
autoHideDuration: 2000
});
} finally {
setLoading(false);
}
};
return (
<>
{loading && (
<>
<Loader />
<CircularLoader />
</>
)}
<MainCard
title={
<Stack
direction={{ xs: 'column', sm: 'row' }}
justifyContent="space-between"
alignItems={{ xs: 'stretch', sm: 'center' }}
spacing={{ xs: 1.5, sm: 0 }}
sx={{ backgroundColor: 'secondary.lighter', width: '100%', height: '100%', p: 2 }}
>
<Typography variant="h3">Edit Miler</Typography>
<Button startIcon={<ArrowBackIcon />} variant="outlined" fullWidth={isMobile} onClick={() => navigate('/nearle/riders')}>
Back to Milers
</Button>
</Stack>
}
>
<Box sx={{ p: { xs: 1.5, sm: 3 } }}>
<Grid container spacing={{ xs: 2, sm: 3 }}>
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="miler-name">Name</InputLabel>
<TextField
fullWidth
id="miler-name"
value={riderdata?.displayname || ''}
onChange={(e) => setRiderdata({ ...riderdata, displayname: e.target.value })}
placeholder="Name"
autoComplete="off"
/>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="miler-phone">Phone Number</InputLabel>
<Stack direction="row" justifyContent="space-between" alignItems="center" spacing={2}>
<Select defaultValue="+91" disabled sx={{ cursor: 'not-allowed' }}>
<MenuItem value="+91">+91</MenuItem>
</Select>
<TextField
type="tel"
id="miler-phone"
fullWidth
placeholder="Phone Number"
onChange={(e) => {
if (e.target.value.toString().length <= 10) {
setRiderdata({ ...riderdata, phone: e.target.value });
}
}}
value={riderdata?.phone || ''}
autoComplete="off"
/>
</Stack>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="miler-hub">Hub</InputLabel>
<Select
id="miler-hub"
fullWidth
displayEmpty
value={riderdata?.hubid ?? ''}
onChange={(e) => setRiderdata({ ...riderdata, hubid: e.target.value })}
renderValue={(selected) => {
if (!selected) return <em>Select hub</em>;
const hub = hubs.find((h) => h.applocationid === selected);
return hub?.locationname || selected;
}}
>
{hubs.map((h) => (
<MenuItem key={h.applocationid} value={h.applocationid}>
{h.locationname}
</MenuItem>
))}
</Select>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="miler-status">Availability Status</InputLabel>
<Select
id="miler-status"
fullWidth
value={riderdata?.availabilitystatus || ''}
onChange={(e) => setRiderdata({ ...riderdata, availabilitystatus: e.target.value })}
>
{AVAILABILITY_OPTIONS.map((status) => (
<MenuItem key={status} value={status}>
{status.replace('_', ' ')}
</MenuItem>
))}
</Select>
</Stack>
</Grid>
</Grid>
</Box>
</MainCard>
<Box
sx={{
position: 'sticky',
bottom: 0,
backgroundColor: 'secondary.lighter',
p: 2,
zIndex: 10,
border: '1px solid',
borderColor: '#E6EBF1',
borderTop: 'none'
}}
>
<Stack direction={{ xs: 'column-reverse', sm: 'row' }} justifyContent="flex-end" spacing={2}>
<Button startIcon={<ArrowBackIcon />} variant="outlined" fullWidth={isMobile} onClick={() => navigate('/nearle/riders')}>
Back to Milers
</Button>
<Button variant="contained" fullWidth={isMobile} onClick={() => updateRider()}>
Update
</Button>
</Stack>
</Box>
</>
);
};
export default EditRider;