updates on the rider and the rider substitution page regarding the crud operations and more change outthere
This commit is contained in:
@@ -45,11 +45,15 @@ const STATUS_META = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function RiderSubstitution({
|
export default function RiderSubstitution({
|
||||||
|
appId,
|
||||||
rows,
|
rows,
|
||||||
|
allRidersList,
|
||||||
substituteRidersList,
|
substituteRidersList,
|
||||||
substituteAssignments,
|
substituteAssignments,
|
||||||
handleAssignSubstitute,
|
handleAssignSubstitute,
|
||||||
onFinalizeSuccess,
|
onFinalizeSuccess,
|
||||||
|
selectedDate,
|
||||||
|
setSelectedDate,
|
||||||
DT,
|
DT,
|
||||||
BRAND,
|
BRAND,
|
||||||
edge,
|
edge,
|
||||||
@@ -58,7 +62,6 @@ export default function RiderSubstitution({
|
|||||||
}) {
|
}) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
|
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
|
||||||
const [selectedDate, setSelectedDate] = useState(dayjs());
|
|
||||||
const [selectedBatch, setSelectedBatch] = useState('all');
|
const [selectedBatch, setSelectedBatch] = useState('all');
|
||||||
const [isFinalizing, setIsFinalizing] = useState(false);
|
const [isFinalizing, setIsFinalizing] = useState(false);
|
||||||
|
|
||||||
@@ -118,9 +121,8 @@ export default function RiderSubstitution({
|
|||||||
}, [selectedDate]);
|
}, [selectedDate]);
|
||||||
|
|
||||||
const filteredRows = React.useMemo(() => {
|
const filteredRows = React.useMemo(() => {
|
||||||
if (selectedBatch === 'all') return rows;
|
return rows;
|
||||||
return rows?.filter((row) => getBatchForTime(row.starttime) === selectedBatch);
|
}, [rows]);
|
||||||
}, [rows, selectedBatch, getBatchForTime]);
|
|
||||||
|
|
||||||
const hasAssignments = Object.values(substituteAssignments || {}).some(
|
const hasAssignments = Object.values(substituteAssignments || {}).some(
|
||||||
(val) => val !== null && val !== undefined
|
(val) => val !== null && val !== undefined
|
||||||
@@ -129,8 +131,37 @@ export default function RiderSubstitution({
|
|||||||
const handleFinalize = async () => {
|
const handleFinalize = async () => {
|
||||||
setIsFinalizing(true);
|
setIsFinalizing(true);
|
||||||
try {
|
try {
|
||||||
const rawTenantId = localStorage.getItem('tenantid');
|
let partnerId = '';
|
||||||
const tenantId = (rawTenantId && rawTenantId !== 'undefined' && rawTenantId !== 'null') ? parseInt(rawTenantId, 10) : 44;
|
try {
|
||||||
|
const rawLocs = localStorage.getItem('applocations');
|
||||||
|
if (rawLocs) {
|
||||||
|
const locs = JSON.parse(rawLocs);
|
||||||
|
const currentLoc = locs.find((l) => l.applocationid === appId);
|
||||||
|
if (currentLoc && currentLoc.partnerid) {
|
||||||
|
partnerId = currentLoc.partnerid;
|
||||||
|
} else {
|
||||||
|
const firstValidLoc = locs.find((l) => l.partnerid);
|
||||||
|
if (firstValidLoc && firstValidLoc.partnerid) {
|
||||||
|
partnerId = firstValidLoc.partnerid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error parsing applocations', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!partnerId) {
|
||||||
|
const savedPartnerId = localStorage.getItem('partnerid');
|
||||||
|
if (savedPartnerId && savedPartnerId !== 'undefined' && savedPartnerId !== 'null') {
|
||||||
|
partnerId = savedPartnerId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!partnerId || partnerId === '0' || partnerId === 0) {
|
||||||
|
partnerId = 44;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tenantId = parseInt(partnerId, 10);
|
||||||
|
|
||||||
const substitutions = Object.entries(substituteAssignments || {})
|
const substitutions = Object.entries(substituteAssignments || {})
|
||||||
.filter((entry) => entry[1] !== null && entry[1] !== undefined)
|
.filter((entry) => entry[1] !== null && entry[1] !== undefined)
|
||||||
@@ -143,7 +174,8 @@ export default function RiderSubstitution({
|
|||||||
absent_rider_name: absentRider?.username || absentRider?.fullname || `Rider #${absentRiderId}`,
|
absent_rider_name: absentRider?.username || absentRider?.fullname || `Rider #${absentRiderId}`,
|
||||||
sub_rider_id: parseInt(subRider.userid, 10),
|
sub_rider_id: parseInt(subRider.userid, 10),
|
||||||
sub_rider_name: subRider.username || subRider.fullname || `Rider #${subRider.userid}`,
|
sub_rider_name: subRider.username || subRider.fullname || `Rider #${subRider.userid}`,
|
||||||
reason: "Scheduled"
|
reason: "Scheduled",
|
||||||
|
batch: selectedBatch
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -315,6 +347,8 @@ export default function RiderSubstitution({
|
|||||||
filteredRows?.map((row, index) => {
|
filteredRows?.map((row, index) => {
|
||||||
const statusMeta = getRowStatusMeta(row);
|
const statusMeta = getRowStatusMeta(row);
|
||||||
const StatusIcon = statusMeta.icon;
|
const StatusIcon = statusMeta.icon;
|
||||||
|
const riderProfile = allRidersList?.find((r) => r.userid === row.userid);
|
||||||
|
const starttime = riderProfile?.starttime || row.starttime;
|
||||||
return (
|
return (
|
||||||
<MobileCard
|
<MobileCard
|
||||||
key={row.userid ?? index}
|
key={row.userid ?? index}
|
||||||
@@ -398,7 +432,7 @@ export default function RiderSubstitution({
|
|||||||
<MobileField label="Substitute" full>
|
<MobileField label="Substitute" full>
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
size="small"
|
size="small"
|
||||||
disabled={isBatchDisabled(getBatchForTime(row.starttime))}
|
disabled={isBatchDisabled(getBatchForTime(starttime))}
|
||||||
options={substituteRidersList || []}
|
options={substituteRidersList || []}
|
||||||
isOptionEqualToValue={(option, value) => option?.userid === value?.userid}
|
isOptionEqualToValue={(option, value) => option?.userid === value?.userid}
|
||||||
getOptionLabel={(option) => `${option.username || option.fullname || ''} (#${option.userid})`}
|
getOptionLabel={(option) => `${option.username || option.fullname || ''} (#${option.userid})`}
|
||||||
@@ -471,6 +505,8 @@ export default function RiderSubstitution({
|
|||||||
filteredRows?.map((row, index) => {
|
filteredRows?.map((row, index) => {
|
||||||
const statusMeta = getRowStatusMeta(row);
|
const statusMeta = getRowStatusMeta(row);
|
||||||
const StatusIcon = statusMeta.icon;
|
const StatusIcon = statusMeta.icon;
|
||||||
|
const riderProfile = allRidersList?.find((r) => r.userid === row.userid);
|
||||||
|
const starttime = riderProfile?.starttime || row.starttime;
|
||||||
return (
|
return (
|
||||||
<TableRow
|
<TableRow
|
||||||
key={row.userid ?? index}
|
key={row.userid ?? index}
|
||||||
@@ -545,7 +581,7 @@ export default function RiderSubstitution({
|
|||||||
<TableCell sx={{ minWidth: 220 }}>
|
<TableCell sx={{ minWidth: 220 }}>
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
size="small"
|
size="small"
|
||||||
disabled={isBatchDisabled(getBatchForTime(row.starttime))}
|
disabled={isBatchDisabled(getBatchForTime(starttime))}
|
||||||
options={substituteRidersList || []}
|
options={substituteRidersList || []}
|
||||||
isOptionEqualToValue={(option, value) => option?.userid === value?.userid}
|
isOptionEqualToValue={(option, value) => option?.userid === value?.userid}
|
||||||
getOptionLabel={(option) => `${option.username || option.fullname || ''} (#${option.userid})`}
|
getOptionLabel={(option) => `${option.username || option.fullname || ''} (#${option.userid})`}
|
||||||
|
|||||||
@@ -23,7 +23,14 @@ import {
|
|||||||
Skeleton,
|
Skeleton,
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
ToggleButtonGroup,
|
ToggleButtonGroup,
|
||||||
ToggleButton
|
ToggleButton,
|
||||||
|
Dialog,
|
||||||
|
DialogTitle,
|
||||||
|
DialogContent,
|
||||||
|
DialogActions,
|
||||||
|
Button,
|
||||||
|
Autocomplete,
|
||||||
|
TextField
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { useTheme } from '@mui/material/styles';
|
import { useTheme } from '@mui/material/styles';
|
||||||
var utc = require('dayjs/plugin/utc');
|
var utc = require('dayjs/plugin/utc');
|
||||||
@@ -51,7 +58,8 @@ import {
|
|||||||
MdAccessTime,
|
MdAccessTime,
|
||||||
MdInventory2,
|
MdInventory2,
|
||||||
MdTwoWheeler,
|
MdTwoWheeler,
|
||||||
MdArrowForward
|
MdArrowForward,
|
||||||
|
MdDelete
|
||||||
} from 'react-icons/md';
|
} from 'react-icons/md';
|
||||||
import LocationAutocomplete from 'components/nearle_components/LocationAutocomplete';
|
import LocationAutocomplete from 'components/nearle_components/LocationAutocomplete';
|
||||||
import PageHeader from 'components/nearle_components/PageHeader';
|
import PageHeader from 'components/nearle_components/PageHeader';
|
||||||
@@ -164,9 +172,101 @@ const Riders = () => {
|
|||||||
const totalCols = roleid == 1 ? 11 : 10;
|
const totalCols = roleid == 1 ? 11 : 10;
|
||||||
const [logsRow, setLogsRow] = useState(null);
|
const [logsRow, setLogsRow] = useState(null);
|
||||||
const [riderLogsdata, setRiderLogsdata] = useState(null);
|
const [riderLogsdata, setRiderLogsdata] = useState(null);
|
||||||
const [historyFromDate, setHistoryFromDate] = useState(dayjs().startOf('month'));
|
const [historyDate, setHistoryDate] = useState(dayjs());
|
||||||
const [historyToDate, setHistoryToDate] = useState(dayjs().endOf('month'));
|
|
||||||
const [historyStatus, setHistoryStatus] = useState('all');
|
const [historyStatus, setHistoryStatus] = useState('all');
|
||||||
|
const [selectedDate, setSelectedDate] = useState(dayjs());
|
||||||
|
const [editingSub, setEditingSub] = useState(null);
|
||||||
|
const [newSubRider, setNewSubRider] = useState(null);
|
||||||
|
|
||||||
|
const getResolvedPartnerId = () => {
|
||||||
|
let partnerId = '';
|
||||||
|
try {
|
||||||
|
const rawLocs = localStorage.getItem('applocations');
|
||||||
|
if (rawLocs) {
|
||||||
|
const locs = JSON.parse(rawLocs);
|
||||||
|
const currentLoc = locs.find((l) => l.applocationid === appId);
|
||||||
|
if (currentLoc && currentLoc.partnerid) {
|
||||||
|
partnerId = currentLoc.partnerid;
|
||||||
|
} else {
|
||||||
|
const firstValidLoc = locs.find((l) => l.partnerid);
|
||||||
|
if (firstValidLoc && firstValidLoc.partnerid) {
|
||||||
|
partnerId = firstValidLoc.partnerid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error parsing applocations', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!partnerId) {
|
||||||
|
const savedPartnerId = localStorage.getItem('partnerid');
|
||||||
|
if (savedPartnerId && savedPartnerId !== 'undefined' && savedPartnerId !== 'null') {
|
||||||
|
partnerId = savedPartnerId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!partnerId || partnerId === '0' || partnerId === 0) {
|
||||||
|
partnerId = 44;
|
||||||
|
}
|
||||||
|
return partnerId;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getHistoryStatusColor = (status) => {
|
||||||
|
const s = (status || '').toLowerCase();
|
||||||
|
if (s === 'cancelled' || s === 'deleted' || s === 'inactive') return '#ef4444'; // Red
|
||||||
|
if (s === 'scheduled') return '#0ea5e9'; // Blue
|
||||||
|
if (s === 'active' || s === 'completed') return '#10b981'; // Green
|
||||||
|
return '#64748b'; // Gray
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteSubstitution = async (row) => {
|
||||||
|
if (window.confirm(`Are you sure you want to delete the substitution for ${row.absent_rider_name}?`)) {
|
||||||
|
try {
|
||||||
|
const resolvedTenantId = row.tenant_id || row.tenantid || getResolvedPartnerId();
|
||||||
|
const url = `${process.env.REACT_APP_URL}/substitutions/${row.id}?tenant_id=${resolvedTenantId}`;
|
||||||
|
const res = await axios.delete(url);
|
||||||
|
if (res.data && res.data.status) {
|
||||||
|
OpenToast('Substitution deleted successfully!', 'success', 2000);
|
||||||
|
} else {
|
||||||
|
OpenToast(res.data?.message || 'Failed to delete substitution.', 'error', 2000);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
OpenToast(err.response?.data?.message || err.message || 'Failed to delete substitution.', 'error', 2000);
|
||||||
|
} finally {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['substitutionsHistory'] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUpdateSubstitutionSubmit = async () => {
|
||||||
|
if (!newSubRider) {
|
||||||
|
OpenToast('Please select a substitute rider', 'error', 2000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const resolvedTenantId = editingSub.tenant_id || editingSub.tenantid || getResolvedPartnerId();
|
||||||
|
const url = `${process.env.REACT_APP_URL}/substitutions/${editingSub.id}`;
|
||||||
|
const payload = {
|
||||||
|
tenant_id: parseInt(resolvedTenantId, 10),
|
||||||
|
sub_rider_id: parseInt(newSubRider.userid, 10),
|
||||||
|
sub_rider_name: newSubRider.username || newSubRider.fullname || `Rider #${newSubRider.userid}`
|
||||||
|
};
|
||||||
|
const res = await axios.put(url, payload);
|
||||||
|
if (res.data && res.data.status) {
|
||||||
|
OpenToast('Substitution updated successfully!', 'success', 2000);
|
||||||
|
} else {
|
||||||
|
OpenToast(res.data?.message || 'Failed to update substitution.', 'error', 2000);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
OpenToast(err.response?.data?.message || err.message || 'Failed to update substitution.', 'error', 2000);
|
||||||
|
} finally {
|
||||||
|
setEditingSub(null);
|
||||||
|
setNewSubRider(null);
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['substitutionsHistory'] });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const [substituteAssignments, setSubstituteAssignments] = useState(() => {
|
const [substituteAssignments, setSubstituteAssignments] = useState(() => {
|
||||||
try {
|
try {
|
||||||
@@ -229,15 +329,18 @@ const Riders = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!partnerId) {
|
if (!partnerId || partnerId === '0' || partnerId === 0) {
|
||||||
partnerId = 44; // final fallback
|
partnerId = 44; // final fallback
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = `${process.env.REACT_APP_URL}/partners/getallriders/?applocationid=${appId}&partnerid=${partnerId}`;
|
const url = `${process.env.REACT_APP_URL}/partners/getallriders/?applocationid=0&partnerid=${partnerId}&pagesize=1000&pageno=1&status=`;
|
||||||
const res = await axios.get(url);
|
const res = await axios.get(url);
|
||||||
const allRiders = res.data.details || [];
|
const allRiders = res.data.details || [];
|
||||||
|
console.log('Substitute query - all riders returned from API:', allRiders.map(r => ({ userid: r.userid, username: r.username, partnerid: r.partnerid })));
|
||||||
const allowedIds = [1121, 772, 1116];
|
const allowedIds = [1121, 772, 1116];
|
||||||
return allRiders.filter((rider) => allowedIds.includes(parseInt(rider.userid)));
|
const filtered = allRiders.filter((rider) => allowedIds.includes(parseInt(rider.userid)));
|
||||||
|
console.log('Substitute query - filtered allowed riders:', filtered);
|
||||||
|
return filtered;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
return [];
|
return [];
|
||||||
@@ -246,10 +349,10 @@ const Riders = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { data: subRidersLogsData, isLoading: subRidersLogsLoading } = useQuery({
|
const { data: subRidersLogsData, isLoading: subRidersLogsLoading } = useQuery({
|
||||||
queryKey: ['subRidersLogs', appId, debouncedSearch],
|
queryKey: ['subRidersLogs', appId, debouncedSearch, selectedDate.format('YYYY-MM-DD')],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
try {
|
try {
|
||||||
const todayStr = dayjs().format('YYYY-MM-DD');
|
const dateStr = selectedDate.format('YYYY-MM-DD');
|
||||||
|
|
||||||
// Resolve partner ID dynamically
|
// Resolve partner ID dynamically
|
||||||
let partnerId = '';
|
let partnerId = '';
|
||||||
@@ -278,11 +381,11 @@ const Riders = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!partnerId) {
|
if (!partnerId || partnerId === '0' || partnerId === 0) {
|
||||||
partnerId = 44;
|
partnerId = 44;
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = `${process.env.REACT_APP_URL2}/partners/getriderlogs/?applocationid=${appId}&partnerid=${partnerId}&fromdate=${todayStr}&todate=${todayStr}&keyword=${debouncedSearch}`;
|
const url = `${process.env.REACT_APP_URL2}/partners/getriderlogs/?applocationid=${appId}&partnerid=${partnerId}&fromdate=${dateStr}&todate=${dateStr}&keyword=${debouncedSearch}`;
|
||||||
const res = await axios.get(url);
|
const res = await axios.get(url);
|
||||||
return res.data.details || [];
|
return res.data.details || [];
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -293,7 +396,7 @@ const Riders = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { data: subHistoryData, isLoading: subHistoryLoading } = useQuery({
|
const { data: subHistoryData, isLoading: subHistoryLoading } = useQuery({
|
||||||
queryKey: ['substitutionsHistory', appId, historyFromDate, historyToDate, historyStatus],
|
queryKey: ['substitutionsHistory', appId, historyDate, historyStatus],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
try {
|
try {
|
||||||
let partnerId = '';
|
let partnerId = '';
|
||||||
@@ -322,11 +425,12 @@ const Riders = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!partnerId) {
|
if (!partnerId || partnerId === '0' || partnerId === 0) {
|
||||||
partnerId = 44;
|
partnerId = 44;
|
||||||
}
|
}
|
||||||
|
|
||||||
let url = `${process.env.REACT_APP_URL}/substitutions?tenant_id=${partnerId}&from_date=${historyFromDate.format('YYYY-MM-DD')}&to_date=${historyToDate.format('YYYY-MM-DD')}`;
|
const dateStr = historyDate.format('YYYY-MM-DD');
|
||||||
|
let url = `${process.env.REACT_APP_URL}/substitutions?tenant_id=${partnerId}&from_date=${dateStr}&to_date=${dateStr}`;
|
||||||
if (historyStatus !== 'all') {
|
if (historyStatus !== 'all') {
|
||||||
url += `&status=${historyStatus}`;
|
url += `&status=${historyStatus}`;
|
||||||
}
|
}
|
||||||
@@ -672,33 +776,14 @@ const Riders = () => {
|
|||||||
<Stack direction="row" spacing={1.5} alignItems="center">
|
<Stack direction="row" spacing={1.5} alignItems="center">
|
||||||
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
label="From Date"
|
label="Select Date"
|
||||||
value={historyFromDate}
|
value={historyDate}
|
||||||
onChange={(newValue) => newValue && setHistoryFromDate(newValue)}
|
onChange={(newValue) => newValue && setHistoryDate(newValue)}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
textField: {
|
textField: {
|
||||||
size: 'small',
|
size: 'small',
|
||||||
sx: {
|
sx: {
|
||||||
width: 150,
|
width: 180,
|
||||||
'& .MuiOutlinedInput-root': {
|
|
||||||
borderRadius: '20px',
|
|
||||||
'& fieldset': { borderColor: DT.borderSubtle },
|
|
||||||
'&:hover fieldset': { borderColor: BRAND },
|
|
||||||
'&.Mui-focused fieldset': { borderColor: BRAND }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<DatePicker
|
|
||||||
label="To Date"
|
|
||||||
value={historyToDate}
|
|
||||||
onChange={(newValue) => newValue && setHistoryToDate(newValue)}
|
|
||||||
slotProps={{
|
|
||||||
textField: {
|
|
||||||
size: 'small',
|
|
||||||
sx: {
|
|
||||||
width: 150,
|
|
||||||
'& .MuiOutlinedInput-root': {
|
'& .MuiOutlinedInput-root': {
|
||||||
borderRadius: '20px',
|
borderRadius: '20px',
|
||||||
'& fieldset': { borderColor: DT.borderSubtle },
|
'& fieldset': { borderColor: DT.borderSubtle },
|
||||||
@@ -757,9 +842,9 @@ const Riders = () => {
|
|||||||
px: 1,
|
px: 1,
|
||||||
py: 0.25,
|
py: 0.25,
|
||||||
borderRadius: 999,
|
borderRadius: 999,
|
||||||
bgcolor: tint('#10b981'),
|
bgcolor: tint(getHistoryStatusColor(row.status)),
|
||||||
border: `1px solid ${edge('#10b981')}`,
|
border: `1px solid ${edge(getHistoryStatusColor(row.status))}`,
|
||||||
color: '#10b981',
|
color: getHistoryStatusColor(row.status),
|
||||||
fontWeight: 800,
|
fontWeight: 800,
|
||||||
fontSize: 10
|
fontSize: 10
|
||||||
}}
|
}}
|
||||||
@@ -841,6 +926,7 @@ const Riders = () => {
|
|||||||
<TableCell>Substitute Rider</TableCell>
|
<TableCell>Substitute Rider</TableCell>
|
||||||
<TableCell>Reason</TableCell>
|
<TableCell>Reason</TableCell>
|
||||||
<TableCell align="center">Status</TableCell>
|
<TableCell align="center">Status</TableCell>
|
||||||
|
<TableCell align="right">Action</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
@@ -908,9 +994,9 @@ const Riders = () => {
|
|||||||
px: 1.5,
|
px: 1.5,
|
||||||
py: 0.5,
|
py: 0.5,
|
||||||
borderRadius: 999,
|
borderRadius: 999,
|
||||||
bgcolor: tint('#10b981'),
|
bgcolor: tint(getHistoryStatusColor(row.status)),
|
||||||
border: `1px solid ${edge('#10b981')}`,
|
border: `1px solid ${edge(getHistoryStatusColor(row.status))}`,
|
||||||
color: '#10b981',
|
color: getHistoryStatusColor(row.status),
|
||||||
fontWeight: 800,
|
fontWeight: 800,
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
textTransform: 'capitalize'
|
textTransform: 'capitalize'
|
||||||
@@ -919,6 +1005,43 @@ const Riders = () => {
|
|||||||
{row.status || 'scheduled'}
|
{row.status || 'scheduled'}
|
||||||
</Box>
|
</Box>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell align="right">
|
||||||
|
<Stack direction="row" spacing={0.75} justifyContent="flex-end">
|
||||||
|
<Tooltip title="Update substitution" placement="top">
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
sx={{
|
||||||
|
bgcolor: soft(BRAND),
|
||||||
|
color: BRAND,
|
||||||
|
border: `1px solid ${edge(BRAND)}`,
|
||||||
|
'&:hover': { bgcolor: BRAND, color: '#fff' }
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
setEditingSub(row);
|
||||||
|
setNewSubRider(null);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MdEdit size={14} />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="Delete substitution" placement="top">
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
sx={{
|
||||||
|
bgcolor: soft('#ef4444'),
|
||||||
|
color: '#ef4444',
|
||||||
|
border: `1px solid ${edge('#ef4444')}`,
|
||||||
|
'&:hover': { bgcolor: '#ef4444', color: '#fff' }
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
handleDeleteSubstitution(row);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MdDelete size={14} />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</Stack>
|
||||||
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))}
|
))}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
@@ -930,10 +1053,13 @@ const Riders = () => {
|
|||||||
<RiderSubstitution
|
<RiderSubstitution
|
||||||
appId={appId}
|
appId={appId}
|
||||||
rows={subRidersLogsData || []}
|
rows={subRidersLogsData || []}
|
||||||
|
allRidersList={rows}
|
||||||
substituteRidersList={substituteRidersList}
|
substituteRidersList={substituteRidersList}
|
||||||
substituteAssignments={substituteAssignments}
|
substituteAssignments={substituteAssignments}
|
||||||
handleAssignSubstitute={handleAssignSubstitute}
|
handleAssignSubstitute={handleAssignSubstitute}
|
||||||
onFinalizeSuccess={handleFinalizeSuccess}
|
onFinalizeSuccess={handleFinalizeSuccess}
|
||||||
|
selectedDate={selectedDate}
|
||||||
|
setSelectedDate={setSelectedDate}
|
||||||
DT={DT}
|
DT={DT}
|
||||||
BRAND={BRAND}
|
BRAND={BRAND}
|
||||||
edge={edge}
|
edge={edge}
|
||||||
@@ -1670,6 +1796,79 @@ const Riders = () => {
|
|||||||
</TableContainer>
|
</TableContainer>
|
||||||
</Paper>
|
</Paper>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<Dialog
|
||||||
|
open={Boolean(editingSub)}
|
||||||
|
onClose={() => setEditingSub(null)}
|
||||||
|
maxWidth="xs"
|
||||||
|
fullWidth
|
||||||
|
PaperProps={{
|
||||||
|
sx: {
|
||||||
|
borderRadius: `${DT.radiusCard / 8}px`,
|
||||||
|
p: 1
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DialogTitle sx={{ fontWeight: 700, pb: 1 }}>Update Substitution</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<Stack spacing={2} sx={{ mt: 1 }}>
|
||||||
|
<Typography variant="body2" sx={{ color: DT.textSecondary }}>
|
||||||
|
Choose a new substitute rider for <strong>{editingSub?.absent_rider_name}</strong>.
|
||||||
|
</Typography>
|
||||||
|
<Autocomplete
|
||||||
|
size="small"
|
||||||
|
options={substituteRidersList || []}
|
||||||
|
isOptionEqualToValue={(option, value) => option?.userid === value?.userid}
|
||||||
|
getOptionLabel={(option) => `${option.username || option.fullname || ''} (#${option.userid})`}
|
||||||
|
value={newSubRider}
|
||||||
|
onChange={(event, newValue) => setNewSubRider(newValue)}
|
||||||
|
renderInput={(params) => (
|
||||||
|
<TextField
|
||||||
|
{...params}
|
||||||
|
placeholder="Select Substitute Rider"
|
||||||
|
sx={{
|
||||||
|
'& .MuiOutlinedInput-root': {
|
||||||
|
borderRadius: '8px',
|
||||||
|
bgcolor: '#f8fafc',
|
||||||
|
'& fieldset': { borderColor: DT.borderSubtle }
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions sx={{ px: 3, pb: 2 }}>
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
onClick={() => setEditingSub(null)}
|
||||||
|
sx={{
|
||||||
|
borderRadius: '8px',
|
||||||
|
textTransform: 'none',
|
||||||
|
fontWeight: 600,
|
||||||
|
borderColor: DT.borderSubtle,
|
||||||
|
color: DT.textSecondary
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
onClick={handleUpdateSubstitutionSubmit}
|
||||||
|
disabled={!newSubRider}
|
||||||
|
sx={{
|
||||||
|
borderRadius: '8px',
|
||||||
|
textTransform: 'none',
|
||||||
|
fontWeight: 600,
|
||||||
|
bgcolor: BRAND,
|
||||||
|
color: '#fff',
|
||||||
|
'&:hover': { bgcolor: '#4D1C61' }
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Save Changes
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user