updates on the dispatch page and redesigned the maximum pages

This commit is contained in:
2026-06-02 13:09:29 +05:30
parent c882dbdcdd
commit 8d0c796ba5
25 changed files with 24405 additions and 4133 deletions

View File

@@ -0,0 +1,154 @@
// Vendored from leaflet-polylineoffset@1.1.1 (MIT).
//
// Why this lives in-tree instead of being an npm dep:
// • The published package would require --legacy-peer-deps because of an
// unrelated React-17 peer-dep conflict elsewhere in the project, and we
// don't want a renderer plugin to force a global resolver flag.
// • It's frozen upstream (no meaningful updates since 2020), tiny, and
// has zero runtime deps besides leaflet (already in package.json).
//
// What it does:
// Monkey-patches L.Polyline so that any path passed with a numeric
// `offset` in pathOptions is rendered shifted perpendicular to its
// direction of travel by that many pixels (positive = right of travel,
// negative = left). Used by Dispatch.js's Compare → Combined view to
// render planned + actual as parallel rails when they share the same
// road geometry; without this they overlap and read as one polyline.
//
// Import once for the side effect:
// import '../../../utils/leafletPolylineOffset';
//
// Then add to any pathOptions:
// pathOptions={{ ..., offset: 5 }}
//
// Plays nicely with both SVG and Canvas renderers.
import L from 'leaflet';
L.PolylineOffset = {
translatePoint(pt, dist, radians) {
return L.point(pt.x + dist * Math.cos(radians), pt.y + dist * Math.sin(radians));
},
offsetPointLine(points, distance) {
const l = points.length;
if (l < 2) {
throw new Error('Line should be defined by at least 2 points');
}
let a = points[0];
let b;
const offsetAngle = Math.PI / 2;
const offsetSegments = [];
for (let i = 1; i < l; i++) {
b = points[i];
// Each segment's offset angle is perpendicular to its direction.
const segAngle = Math.atan2(b.y - a.y, b.x - a.x);
offsetSegments.push({
offsetAngle: segAngle - offsetAngle,
original: [a, b],
offset: [
this.translatePoint(a, distance, segAngle - offsetAngle),
this.translatePoint(b, distance, segAngle - offsetAngle)
]
});
a = b;
}
return offsetSegments;
},
// Find the intersection of two segments by extending them to infinity
// along their direction, then walking along segment 1 by parameter t.
// Returns null when the segments are parallel (no intersection).
intersection(l1a, l1b, l2a, l2b) {
const line1 = this.segmentAsVector(l1a, l1b);
const line2 = this.segmentAsVector(l2a, l2b);
const denom = -line2.x * line1.y + line1.x * line2.y;
if (denom === 0) return null;
const s = (-line1.y * (l1a.x - l2a.x) + line1.x * (l1a.y - l2a.y)) / denom;
const t = (line2.x * (l1a.y - l2a.y) - line2.y * (l1a.x - l2a.x)) / denom;
if (s >= 0 && s <= 1 && t >= 0 && t <= 1) {
return L.point(l1a.x + t * line1.x, l1a.y + t * line1.y);
}
return null;
},
segmentAsVector(a, b) {
return L.point(b.x - a.x, b.y - a.y);
},
// Walk the offset segments and join adjacent ones at their intersection
// points (mitered corners). When two consecutive segments don't intersect
// within their bounds (sharp turn, or co-linear), fall back to the offset
// endpoint so the polyline doesn't gap.
joinLineSegments(segments) {
const joined = [];
let last = segments[0].offset;
joined.push(last[0]);
for (let i = 1; i < segments.length; i++) {
const next = segments[i].offset;
const inter = this.intersection(last[0], last[1], next[0], next[1]);
if (inter) {
joined.push(inter);
} else {
joined.push(last[1]);
}
last = next;
}
joined.push(last[1]);
return joined;
},
offsetPoints(points, offset) {
if (!points || points.length < 2) return points;
const offsets = this.offsetPointLine(points, offset);
return this.joinLineSegments(offsets);
},
// Operates on a ring of LatLngs by projecting → offsetting → unprojecting,
// since leaflet polyline math is in screen pixels but our points are LatLng.
offsetLatLngs(map, latlngs, offset) {
const points = latlngs.map((ll) => map.latLngToLayerPoint(ll));
const offsetPts = this.offsetPoints(points, offset);
return offsetPts.map((p) => map.layerPointToLatLng(p));
}
};
// Patch Polyline._projectLatlngs (used by both SVG and Canvas renderers) so
// that when an offset is set, the projected ring is offset before clipping.
// We keep the original on _projectLatlngsOriginal so we can call through.
const originalProject = L.Polyline.prototype._projectLatlngs;
L.Polyline.prototype._projectLatlngs = function patchedProject(latlngs, result, projectedBounds) {
const offset = this.options.offset;
if (!offset || typeof offset !== 'number') {
return originalProject.call(this, latlngs, result, projectedBounds);
}
// Recurse for multi-ring polylines (shouldn't happen for simple lines,
// but the leaflet API allows it).
const flat = latlngs[0] instanceof L.LatLng;
if (!flat) {
for (let i = 0; i < latlngs.length; i++) {
this._projectLatlngs(latlngs[i], result, projectedBounds);
}
return undefined;
}
const projected = latlngs.map((ll) => this._map.latLngToLayerPoint(ll));
const offsetted = L.PolylineOffset.offsetPoints(projected, offset);
// Update projectedBounds with each offset point so the renderer's
// viewport-clipping check still works.
for (let i = 0; i < offsetted.length; i++) {
projectedBounds.extend(offsetted[i]);
}
result.push(offsetted);
return undefined;
};
export default L;

66
src/utils/logger.js Normal file
View File

@@ -0,0 +1,66 @@
const LOG_LEVELS = {
DEBUG: 0,
INFO: 1,
WARN: 2,
ERROR: 3,
};
// Default log level based on environment
const currentEnv = process.env.NODE_ENV || 'development';
const isDev = currentEnv === 'development';
const GLOBAL_LOG_LEVEL = isDev ? LOG_LEVELS.DEBUG : LOG_LEVELS.WARN;
const style = (bg, color) => `background: ${bg}; color: ${color}; padding: 2px 5px; border-radius: 4px; font-weight: bold;`;
const PREFIX = '%c[NearlExpress]';
const PREFIX_STYLE = style('#2563eb', '#ffffff');
// Capture original console methods before any global overrides occur
const originalLog = console.log;
const originalWarn = console.warn || console.log;
const originalError = console.error || console.log;
const print = (levelName, args, labelStyle) => {
const levelValue = LOG_LEVELS[levelName];
if (levelValue < GLOBAL_LOG_LEVEL) return;
const [message, ...extra] = args;
const isMessageString = typeof message === 'string';
const formatPrefix = `${PREFIX}%c ${levelName}`;
const styles = [PREFIX_STYLE, labelStyle];
const consoleMethod =
levelName === 'ERROR' ? originalError :
levelName === 'WARN' ? originalWarn :
originalLog;
if (isMessageString) {
consoleMethod(
`${formatPrefix}%c ${message}`,
...styles,
'color: inherit;',
...extra
);
} else {
// If first argument is an object/array, preserve raw interactive log
consoleMethod(
`${formatPrefix}`,
...styles,
message,
...extra
);
}
};
const logger = {
debug: (...args) => print('DEBUG', args, style('#64748b', '#ffffff')),
info: (...args) => print('INFO', args, style('#10b981', '#ffffff')),
warn: (...args) => print('WARN', args, style('#f59e0b', '#ffffff')),
error: (...args) => {
print('ERROR', args, style('#ef4444', '#ffffff'));
// Future expansion hook: e.g., Sentry.captureException(args[0]);
},
};
export default logger;