feat(routing): enable elevation profile and steep slope/incline warnings in routing API and frontend

This commit is contained in:
Hamza-Ayed
2026-08-18 12:10:32 +03:00
parent 06543ee8e0
commit 57e3da7ea4
12 changed files with 599 additions and 100 deletions
+80 -9
View File
@@ -392,18 +392,89 @@ function App() {
{/* Route Summary Card */}
{routeData && (
<div className="route-summary glass-morphism" style={{ marginTop: '15px', padding: '12px', borderRadius: '8px', background: 'rgba(15, 23, 42, 0.6)' }}>
<h4 style={{ margin: '0 0 8px 0', fontSize: '0.9rem', color: '#38bdf8' }}>Route Overview / تفاصيل المسار</h4>
<div style={{ display: 'flex', justifyContent: 'space-between', fontSize: '0.85rem', marginBottom: '4px' }}>
<span style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
<Gauge size={13} color="#38bdf8" />
{(Number(routeData.distance || 0) / 1000).toFixed(1)} km
<div className="route-summary glass-morphism" style={{ marginTop: '15px', padding: '14px', borderRadius: '10px', background: 'rgba(15, 23, 42, 0.75)', border: '1px solid rgba(56, 189, 248, 0.3)' }}>
<h4 style={{ margin: '0 0 10px 0', fontSize: '0.92rem', color: '#38bdf8', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<span>{routeData.routeName || 'تفاصيل المسار'}</span>
<span style={{ fontSize: '0.75rem', background: 'rgba(56, 189, 248, 0.15)', color: '#38bdf8', padding: '2px 8px', borderRadius: 999 }}>
{routeData.tags ? routeData.tags[0] : 'FASTEST'}
</span>
<span style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
<Clock size={13} color="#22c55e" />
{Math.round(Number(routeData.time || routeData.duration || 0) / 60000)} min
</h4>
<div style={{ display: 'flex', justifyContent: 'space-between', fontSize: '0.85rem', marginBottom: '8px' }}>
<span style={{ display: 'flex', alignItems: 'center', gap: '4px', color: '#e2e8f0' }}>
<Gauge size={14} color="#38bdf8" />
{(Number(routeData.distance || 0) / 1000).toFixed(1)} كم
</span>
<span style={{ display: 'flex', alignItems: 'center', gap: '4px', color: '#4ade80' }}>
<Clock size={14} color="#22c55e" />
{Math.round(Number(routeData.duration || 0) / 60)} دقيقة
</span>
</div>
{/* Elevation & Slope Summary */}
{routeData.elevationSummary && (
<div style={{ marginTop: 10, paddingTop: 8, borderTop: '1px solid rgba(255,255,255,0.08)', fontSize: '0.78rem' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', color: '#94a3b8', marginBottom: 4 }}>
<span>📈 إجمالي الصعود: <strong style={{ color: '#4ade80' }}>+{routeData.elevationSummary.totalAscentMeters}م</strong></span>
<span>📉 إجمالي الهبوط: <strong style={{ color: '#38bdf8' }}>-{routeData.elevationSummary.totalDescentMeters}م</strong></span>
</div>
{(routeData.elevationSummary.maxInclinePercent > 0 || routeData.elevationSummary.maxDeclinePercent < 0) && (
<div style={{ display: 'flex', justifyContent: 'space-between', color: '#94a3b8' }}>
<span>أقصى صعود: <strong style={{ color: '#fbbf24' }}>+{routeData.elevationSummary.maxInclinePercent}%</strong></span>
<span>أقصى انحدار: <strong style={{ color: '#f87171' }}>{routeData.elevationSummary.maxDeclinePercent}%</strong></span>
</div>
)}
</div>
)}
{/* Steep Slope Warnings Alert */}
{routeData.elevationSummary?.steepWarnings && routeData.elevationSummary.steepWarnings.length > 0 && (
<div style={{
marginTop: 10,
padding: '8px 10px',
background: 'rgba(239, 68, 68, 0.15)',
border: '1px solid rgba(239, 68, 68, 0.35)',
borderRadius: 8,
fontSize: '0.75rem',
color: '#fca5a5'
}}>
<div style={{ fontWeight: 700, display: 'flex', alignItems: 'center', gap: 6, marginBottom: 4 }}>
<AlertTriangle size={13} color="#ef4444" />
<span>تحذير تضاريسي للمركبات الثقيلة:</span>
</div>
{routeData.elevationSummary.steepWarnings.slice(0, 2).map((w: any, idx: number) => (
<div key={idx} style={{ marginTop: 2 }}>
• {w.street ? `شارع ${w.street}: ` : ''}{w.slopePercent > 0 ? `صعود حاد (+${w.slopePercent}%)` : `منحدر جبلي شديد (${w.slopePercent}%)`}
</div>
))}
</div>
)}
{/* Turn by turn expandable instructions */}
{routeData.instructions && routeData.instructions.length > 0 && (
<details style={{ marginTop: 10, fontSize: '0.75rem', color: '#cbd5e1' }}>
<summary style={{ cursor: 'pointer', color: '#38bdf8', fontWeight: 600, padding: '4px 0' }}>
عرض خطوات المسار بالتفصيل ({routeData.instructions.length} خطوة)
</summary>
<div style={{ maxHeight: 180, overflowY: 'auto', marginTop: 6, paddingRight: 4, display: 'flex', flexDirection: 'column', gap: 4 }}>
{routeData.instructions.map((inst: any, idx: number) => (
<div key={idx} style={{
padding: '4px 6px',
borderRadius: 4,
background: inst.slopeWarning ? 'rgba(245, 158, 11, 0.12)' : 'rgba(255,255,255,0.03)',
borderRight: inst.slopeWarning ? '3px solid #f59e0b' : '1px solid transparent'
}}>
<div style={{ fontWeight: inst.slopeWarning ? 700 : 400, color: inst.slopeWarning ? '#fbbf24' : '#e2e8f0' }}>
{idx + 1}. {inst.text}
</div>
<div style={{ fontSize: '0.7rem', color: '#94a3b8' }}>
{inst.distance ? `${Math.round(inst.distance)}م` : ''} {inst.slopePercent ? `| انحدار: ${inst.slopePercent}%` : ''}
</div>
</div>
))}
</div>
</details>
)}
</div>
)}