This commit is contained in:
@@ -1,11 +1,10 @@
|
|||||||
import React, {useState, useEffect} from "react";
|
import React, {useState, useEffect} from "react";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { CustomerService, SignatureRequestService } from "../../services";
|
import { CustomerService, SignatureRequestService } from "../../services";
|
||||||
import SignatureCanvas from 'react-signature-canvas';
|
import SignatureCanvas from 'react-signature-canvas';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
const DriverSignature = () => {
|
const DriverSignature = () => {
|
||||||
const navigate = useNavigate();
|
|
||||||
const urlParams = useParams();
|
const urlParams = useParams();
|
||||||
const [signatureRequest, setSignatureRequest] = useState(undefined);
|
const [signatureRequest, setSignatureRequest] = useState(undefined);
|
||||||
|
|
||||||
@@ -19,28 +18,45 @@ const DriverSignature = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const submitSignature = () => {
|
const submitSignature = () => {
|
||||||
var fabricCanvas = document.getElementsByClassName('sigCanvas')[0]; //real ID here
|
const fabricCanvas = document.getElementsByClassName('sigCanvas')[0];
|
||||||
var scaledCanvas = document.createElement('canvas'); //off-screen canvas
|
if (!fabricCanvas || !signatureRequest?.id) {
|
||||||
|
window.alert('Signature request is not ready. Please refresh and try again.');
|
||||||
scaledCanvas.width = 400; //size of new canvas, make sure they are proportional
|
return;
|
||||||
scaledCanvas.height = 200; //compared to original canvas
|
}
|
||||||
|
const routeId = `${signatureRequest?.route_id || ''}`.trim();
|
||||||
// scale original image to new canvas
|
const driverId = `${signatureRequest?.driver_id || ''}`.trim();
|
||||||
var ctx = scaledCanvas.getContext('2d');
|
|
||||||
ctx.drawImage(fabricCanvas, 0, 0, scaledCanvas.width, scaledCanvas.height);
|
|
||||||
const dateArr = moment(signatureRequest?.route_date)?.format('MM/DD/YYYY')?.split('/') || [];
|
const dateArr = moment(signatureRequest?.route_date)?.format('MM/DD/YYYY')?.split('/') || [];
|
||||||
const fileName = `${signatureRequest?.route_id}_${signatureRequest?.driver_id}_${dateArr[0]}_${dateArr[1]}`;
|
const month = `${dateArr[0] || ''}`.trim();
|
||||||
|
const day = `${dateArr[1] || ''}`.trim();
|
||||||
|
if (!routeId || !driverId || !month || !day || month.toLowerCase().includes('invalid')) {
|
||||||
|
window.alert('Invalid route signature key. Please ask admin to regenerate signature link.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const fileName = `${routeId}_${driverId}_${month}_${day}`;
|
||||||
|
const scaledCanvas = document.createElement('canvas');
|
||||||
|
|
||||||
|
scaledCanvas.width = 400;
|
||||||
|
scaledCanvas.height = 200;
|
||||||
|
|
||||||
|
const ctx = scaledCanvas.getContext('2d');
|
||||||
|
ctx.drawImage(fabricCanvas, 0, 0, scaledCanvas.width, scaledCanvas.height);
|
||||||
scaledCanvas.toBlob(function(blob) {
|
scaledCanvas.toBlob(function(blob) {
|
||||||
|
if (!blob) {
|
||||||
|
window.alert('Failed to read signature image. Please draw signature again.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', blob, `${fileName}.jpg`);
|
formData.append('file', blob, `${fileName}.jpg`);
|
||||||
|
|
||||||
CustomerService.uploadAvatar(fileName, formData).then(() => {
|
CustomerService.uploadAvatar(fileName, formData).then(() => {
|
||||||
SignatureRequestService.updateSignatureRequest(signatureRequest?.id, { status: 'done'}).then(data => {
|
SignatureRequestService.updateSignatureRequest(signatureRequest?.id, { status: 'done'}).then(() => {
|
||||||
SignatureRequestService.getSignatureRequestById(urlParams.id).then((data) => {
|
SignatureRequestService.getSignatureRequestById(urlParams.id).then((data) => {
|
||||||
setSignatureRequest(data?.data);
|
setSignatureRequest(data?.data);
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
}).catch(() => {
|
||||||
|
window.alert('Failed to upload signature. Please try again.');
|
||||||
|
});
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,17 +67,64 @@ const RouteReportWithSignature = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const dateArr = moment(currentRoute?.schedule_date)?.format('MM/DD/YYYY')?.split('/') || [];
|
let isMounted = true;
|
||||||
|
const toDateTokens = (dateValue) => {
|
||||||
CustomerService.getAvatar(`${currentRoute?.id}_${currentRoute?.driver}_${dateArr[0]}_${dateArr[1]}`).then(data => {
|
const tokenSet = new Set();
|
||||||
setSignature(data.data);
|
const localFormatted = moment(dateValue)?.format('MM/DD/YYYY');
|
||||||
});
|
const utcFormatted = moment.utc(dateValue)?.format('MM/DD/YYYY');
|
||||||
|
[localFormatted, utcFormatted].forEach((formatted) => {
|
||||||
|
if (!formatted || formatted.toLowerCase().includes('invalid')) return;
|
||||||
|
const [month, day] = formatted.split('/');
|
||||||
|
if (month && day) {
|
||||||
|
tokenSet.add(`${month}_${day}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return Array.from(tokenSet).map((token) => token.split('_'));
|
||||||
|
};
|
||||||
|
const loadSignature = async () => {
|
||||||
|
if (!currentRoute?.id || !currentRoute?.schedule_date) {
|
||||||
|
if (isMounted) setSignature(undefined);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const routeId = currentRoute.id;
|
||||||
|
const requestRes = await SignatureRequestService.getAllSignatureRequests({ route_id: routeId }).catch(() => ({ data: [] }));
|
||||||
|
const requestList = requestRes?.data || [];
|
||||||
|
const driverIds = Array.from(new Set([
|
||||||
|
currentRoute?.driver,
|
||||||
|
...requestList.map((item) => item?.driver_id)
|
||||||
|
].filter(Boolean)));
|
||||||
|
const datePairs = [];
|
||||||
|
[currentRoute?.schedule_date, ...requestList.map((item) => item?.route_date)].forEach((dateValue) => {
|
||||||
|
toDateTokens(dateValue).forEach((pair) => datePairs.push(pair));
|
||||||
|
});
|
||||||
|
const uniqueDatePairs = Array.from(new Set(datePairs.map((pair) => `${pair[0]}_${pair[1]}`)))
|
||||||
|
.map((token) => token.split('_'));
|
||||||
|
for (const driverId of driverIds) {
|
||||||
|
for (const [month, day] of uniqueDatePairs) {
|
||||||
|
try {
|
||||||
|
const data = await CustomerService.getAvatar(`${routeId}_${driverId}_${month}_${day}`);
|
||||||
|
if (data?.data) {
|
||||||
|
if (isMounted) setSignature(data.data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Continue trying candidate keys.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isMounted) setSignature(undefined);
|
||||||
|
};
|
||||||
|
|
||||||
|
loadSignature();
|
||||||
CustomerService.getAvatar(`center_director_signature_site_${site}`).then(data => {
|
CustomerService.getAvatar(`center_director_signature_site_${site}`).then(data => {
|
||||||
if (data?.data) {
|
if (data?.data) {
|
||||||
setDirectorSignature(data?.data)
|
setDirectorSignature(data?.data)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [currentRoute]);
|
return () => {
|
||||||
|
isMounted = false;
|
||||||
|
};
|
||||||
|
}, [currentRoute?.id, currentRoute?.driver, currentRoute?.schedule_date, site]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
CustomerService.getAllCustomers()
|
CustomerService.getAllCustomers()
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useNavigate, useSearchParams } from "react-router-dom";
|
|||||||
import { selectInboundRoutes, selectTomorrowAllRoutes, selectTomorrowInboundRoutes, selectTomorrowOutboundRoutes, selectHistoryInboundRoutes, selectHistoryRoutes, selectHistoryOutboundRoutes, selectOutboundRoutes, selectAllRoutes, selectAllActiveVehicles, selectAllActiveDrivers, transRoutesSlice } from "./../../store";
|
import { selectInboundRoutes, selectTomorrowAllRoutes, selectTomorrowInboundRoutes, selectTomorrowOutboundRoutes, selectHistoryInboundRoutes, selectHistoryRoutes, selectHistoryOutboundRoutes, selectOutboundRoutes, selectAllRoutes, selectAllActiveVehicles, selectAllActiveDrivers, transRoutesSlice } from "./../../store";
|
||||||
import RoutesSection from "./RoutesSection";
|
import RoutesSection from "./RoutesSection";
|
||||||
import PersonnelSection from "./PersonnelSection";
|
import PersonnelSection from "./PersonnelSection";
|
||||||
import { AuthService, CustomerService, TransRoutesService, DriverService, EventsService, DailyRoutesTemplateService, ReportService } from "../../services";
|
import { AuthService, CustomerService, TransRoutesService, DriverService, EventsService, DailyRoutesTemplateService, ReportService, SignatureRequestService } from "../../services";
|
||||||
import { PERSONAL_ROUTE_STATUS, ROUTE_STATUS, CUSTOMER_TYPE_TEXT, PERSONAL_ROUTE_STATUS_TEXT, PICKUP_STATUS, PICKUP_STATUS_TEXT, REPORT_TYPE } from "../../shared";
|
import { PERSONAL_ROUTE_STATUS, ROUTE_STATUS, CUSTOMER_TYPE_TEXT, PERSONAL_ROUTE_STATUS_TEXT, PICKUP_STATUS, PICKUP_STATUS_TEXT, REPORT_TYPE } from "../../shared";
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import DatePicker from "react-datepicker";
|
import DatePicker from "react-datepicker";
|
||||||
@@ -710,16 +710,52 @@ const RoutesDashboard = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const toDateTokens = (dateValue) => {
|
||||||
|
const tokenSet = new Set();
|
||||||
|
const localFormatted = moment(dateValue)?.format('MM/DD/YYYY');
|
||||||
|
const utcFormatted = moment.utc(dateValue)?.format('MM/DD/YYYY');
|
||||||
|
[localFormatted, utcFormatted].forEach((formatted) => {
|
||||||
|
if (!formatted || formatted.toLowerCase().includes('invalid')) return;
|
||||||
|
const [month, day] = formatted.split('/');
|
||||||
|
if (month && day) {
|
||||||
|
tokenSet.add(`${month}_${day}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return Array.from(tokenSet).map((token) => token.split('_'));
|
||||||
|
};
|
||||||
|
const resolveRouteSignature = async (routeItem) => {
|
||||||
|
const requestRes = await SignatureRequestService.getAllSignatureRequests({ route_id: routeItem?.id }).catch(() => ({ data: [] }));
|
||||||
|
const requestList = requestRes?.data || [];
|
||||||
|
const driverIds = Array.from(new Set([
|
||||||
|
routeItem?.driver,
|
||||||
|
...requestList.map((item) => item?.driver_id)
|
||||||
|
].filter(Boolean)));
|
||||||
|
const datePairs = [];
|
||||||
|
[routeItem?.schedule_date, dateSelected, ...requestList.map((item) => item?.route_date)].forEach((dateValue) => {
|
||||||
|
toDateTokens(dateValue).forEach((pair) => datePairs.push(pair));
|
||||||
|
});
|
||||||
|
const uniqueDatePairs = Array.from(new Set(datePairs.map((pair) => `${pair[0]}_${pair[1]}`)))
|
||||||
|
.map((token) => token.split('_'));
|
||||||
|
for (const driverId of driverIds) {
|
||||||
|
for (const [month, day] of uniqueDatePairs) {
|
||||||
|
try {
|
||||||
|
const result = await CustomerService.getAvatar(`${routeItem.id}_${driverId}_${month}_${day}`);
|
||||||
|
if (result?.data) {
|
||||||
|
return result.data;
|
||||||
|
}
|
||||||
|
} catch (ex) {
|
||||||
|
// Continue trying candidate keys.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
TransRoutesService.getAll(moment(dateSelected)?.format('MM/DD/YYYY')).then(data => {
|
TransRoutesService.getAll(moment(dateSelected)?.format('MM/DD/YYYY')).then(data => {
|
||||||
const routesResults = data.data;
|
const routesResults = data.data;
|
||||||
const finalRoutes = routesResults.map(async (routeItem) => {
|
const finalRoutes = routesResults.map(async (routeItem) => {
|
||||||
const dateArr = moment(dateSelected)?.format('MM/DD/YYYY')?.split('/') || [];
|
const signature = await resolveRouteSignature(routeItem);
|
||||||
try {
|
return signature ? Object.assign({}, routeItem, {signature}) : routeItem;
|
||||||
const result = await CustomerService.getAvatar(`${routeItem.id}_${routeItem.driver}_${dateArr[0]}_${dateArr[1]}`);
|
|
||||||
return result?.data ? Object.assign({}, routeItem, {signature: result?.data}) : routeItem;
|
|
||||||
} catch (ex) {
|
|
||||||
return routeItem;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
Promise.all(finalRoutes).then(finalRoutesData => {
|
Promise.all(finalRoutes).then(finalRoutesData => {
|
||||||
setRoutesForSignature(finalRoutesData);
|
setRoutesForSignature(finalRoutesData);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, {useState, useEffect} from "react";
|
import React, {useState, useEffect} from "react";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { CustomerService, DriverService, EmployeeService, EventsService, TransRoutesService } from "../../services";
|
import { CustomerService, DriverService, EmployeeService, EventsService, SignatureRequestService, TransRoutesService } from "../../services";
|
||||||
import { selectAllActiveDrivers, selectAllActiveVehicles } from "./../../store";
|
import { selectAllActiveDrivers, selectAllActiveVehicles } from "./../../store";
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import DatePicker from "react-datepicker";
|
import DatePicker from "react-datepicker";
|
||||||
@@ -95,6 +95,47 @@ const RouteSignatureList = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const site = EventsService.site;
|
const site = EventsService.site;
|
||||||
|
const toDateTokens = (dateValue) => {
|
||||||
|
const tokenSet = new Set();
|
||||||
|
const localFormatted = moment(dateValue)?.format('MM/DD/YYYY');
|
||||||
|
const utcFormatted = moment.utc(dateValue)?.format('MM/DD/YYYY');
|
||||||
|
[localFormatted, utcFormatted].forEach((formatted) => {
|
||||||
|
if (!formatted || formatted.toLowerCase().includes('invalid')) return;
|
||||||
|
const [month, day] = formatted.split('/');
|
||||||
|
if (month && day) {
|
||||||
|
tokenSet.add(`${month}_${day}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return Array.from(tokenSet).map((token) => token.split('_'));
|
||||||
|
};
|
||||||
|
const resolveRouteSignature = async (routeItem) => {
|
||||||
|
const requestRes = await SignatureRequestService.getAllSignatureRequests({ route_id: routeItem?.id }).catch(() => ({ data: [] }));
|
||||||
|
const requestList = requestRes?.data || [];
|
||||||
|
const driverIds = Array.from(new Set([
|
||||||
|
routeItem?.driver,
|
||||||
|
...requestList.map((item) => item?.driver_id)
|
||||||
|
].filter(Boolean)));
|
||||||
|
const datePairs = [];
|
||||||
|
[routeItem?.schedule_date, dateSelected, ...requestList.map((item) => item?.route_date)].forEach((dateValue) => {
|
||||||
|
toDateTokens(dateValue).forEach((pair) => datePairs.push(pair));
|
||||||
|
});
|
||||||
|
const uniqueDatePairs = Array.from(new Set(datePairs.map((pair) => `${pair[0]}_${pair[1]}`)))
|
||||||
|
.map((token) => token.split('_'));
|
||||||
|
for (const driverId of driverIds) {
|
||||||
|
for (const [month, day] of uniqueDatePairs) {
|
||||||
|
try {
|
||||||
|
const result = await CustomerService.getAvatar(`${routeItem.id}_${driverId}_${month}_${day}`);
|
||||||
|
if (result?.data) {
|
||||||
|
return result.data;
|
||||||
|
}
|
||||||
|
} catch (ex) {
|
||||||
|
// Continue trying candidate keys.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
CustomerService.getAvatar(`center_director_signature_site_${site}`).then(data => {
|
CustomerService.getAvatar(`center_director_signature_site_${site}`).then(data => {
|
||||||
if (data?.data) {
|
if (data?.data) {
|
||||||
setDirectorSignature(data?.data)
|
setDirectorSignature(data?.data)
|
||||||
@@ -103,13 +144,8 @@ const RouteSignatureList = () => {
|
|||||||
TransRoutesService.getAll(moment(dateSelected)?.format('MM/DD/YYYY')).then(data => {
|
TransRoutesService.getAll(moment(dateSelected)?.format('MM/DD/YYYY')).then(data => {
|
||||||
const routesResults = data.data;
|
const routesResults = data.data;
|
||||||
const finalRoutes = routesResults.map(async (routeItem) => {
|
const finalRoutes = routesResults.map(async (routeItem) => {
|
||||||
const dateArr = moment(dateSelected)?.format('MM/DD/YYYY')?.split('/') || [];
|
const signature = await resolveRouteSignature(routeItem);
|
||||||
try {
|
return signature ? Object.assign({}, routeItem, {signature}) : routeItem;
|
||||||
const result = await CustomerService.getAvatar(`${routeItem.id}_${routeItem.driver}_${dateArr[0]}_${dateArr[1]}`);
|
|
||||||
return result?.data ? Object.assign({}, routeItem, {signature: result?.data}) : routeItem;
|
|
||||||
} catch (ex) {
|
|
||||||
return routeItem;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
Promise.all(finalRoutes).then(finalRoutesData => {
|
Promise.all(finalRoutes).then(finalRoutesData => {
|
||||||
setRoutes(finalRoutesData);
|
setRoutes(finalRoutesData);
|
||||||
|
|||||||
Reference in New Issue
Block a user