338 lines
15 KiB
XML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<template id="uber_tracking_page" name="Uber Delivery Tracking">
<t t-call="web.layout">
<t t-set="head_tracking">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Uber Delivery Tracking <t t-esc="order_ref"/></title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: #f0f2f5;
min-height: 100vh;
}
/* ── Top Bar ── */
.track-header {
background: linear-gradient(135deg, #06b6d4 0%, #0369a1 100%);
color: #fff;
padding: 18px 24px;
display: flex;
align-items: center;
gap: 14px;
box-shadow: 0 3px 12px rgba(0,0,0,0.2);
}
.track-header .uber-logo {
font-size: 26px; font-weight: 900; letter-spacing: -1px;
background: #fff;
color: #000;
border-radius: 8px;
padding: 4px 12px;
}
.track-header h1 { font-size: 16px; font-weight: 600; opacity: .9; }
.track-header .order-ref {
margin-left: auto;
background: rgba(255,255,255,0.2);
border-radius: 20px;
padding: 4px 14px;
font-size: 13px;
font-weight: 700;
letter-spacing: .5px;
}
/* ── Status Banner ── */
.status-banner {
margin: 16px 20px 0;
border-radius: 14px;
padding: 14px 20px;
display: flex;
align-items: center;
gap: 12px;
font-weight: 700;
font-size: 15px;
}
.status-warning { background: #fef3c7; color: #92400e; border: 2px solid #fbbf24; }
.status-info { background: #dbeafe; color: #1e40af; border: 2px solid #93c5fd; }
.status-primary { background: #e0f2fe; color: #0369a1; border: 2px solid #38bdf8; }
.status-success { background: #d1fae5; color: #065f46; border: 2px solid #34d399; }
.status-danger { background: #fee2e2; color: #991b1b; border: 2px solid #fca5a5; }
.status-secondary{ background: #f1f5f9; color: #475569; border: 2px solid #94a3b8; }
.pulse-dot {
width: 12px; height: 12px; border-radius: 50%;
background: currentColor; flex-shrink: 0;
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0%,100% { opacity: 1; transform: scale(1); }
50% { opacity: .5; transform: scale(1.3); }
}
/* ── Card Grid ── */
.cards-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 14px;
margin: 14px 20px;
}
@media(max-width: 640px){ .cards-row{ grid-template-columns: 1fr; } }
.location-card {
background: #fff;
border-radius: 16px;
padding: 18px 20px;
box-shadow: 0 2px 10px rgba(0,0,0,.07);
border-left: 5px solid #06b6d4;
position: relative;
}
.location-card.dropoff { border-left-color: #f59e0b; }
.card-icon {
width: 40px; height: 40px; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-size: 18px; margin-bottom: 10px;
}
.card-icon.pickup-icon { background: #cffafe; }
.card-icon.dropoff-icon { background: #fef3c7; }
.card-label {
font-size: 11px; font-weight: 700; text-transform: uppercase;
letter-spacing: 1px; color: #64748b; margin-bottom: 4px;
}
.card-title { font-size: 15px; font-weight: 700; color: #0f172a; margin-bottom: 6px; }
.card-addr { font-size: 13px; color: #475569; line-height: 1.5; }
.card-phone { font-size: 12px; color: #64748b; margin-top: 6px; }
/* ── Map ── */
.map-section {
margin: 0 20px 14px;
border-radius: 16px;
overflow: hidden;
box-shadow: 0 4px 16px rgba(0,0,0,.12);
position: relative;
}
.map-section iframe {
width: 100%; height: 380px; border: 0; display: block;
}
.map-overlay-btns {
position: absolute; top: 12px; right: 12px;
display: flex; flex-direction: column; gap: 8px;
}
.map-btn {
background: #fff;
border: none; border-radius: 10px;
padding: 8px 14px;
font-size: 13px; font-weight: 600;
box-shadow: 0 2px 8px rgba(0,0,0,.15);
cursor: pointer; text-decoration: none;
color: #0f172a; display: flex; align-items: center; gap: 6px;
transition: transform .15s, box-shadow .15s;
}
.map-btn:hover { transform: translateY(-1px); box-shadow: 0 4px 14px rgba(0,0,0,.2); color: #0f172a; }
.map-btn.uber-btn { background: #000; color: #fff; }
/* ── Action Buttons ── */
.action-bar {
display: flex; gap: 12px; margin: 0 20px 20px; flex-wrap: wrap;
}
.btn-full {
flex: 1; min-width: 140px;
padding: 14px 20px;
border-radius: 14px;
border: none; cursor: pointer;
font-size: 14px; font-weight: 700;
display: flex; align-items: center; justify-content: center;
gap: 8px; text-decoration: none;
transition: transform .15s, box-shadow .15s;
}
.btn-full:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(0,0,0,.15); }
.btn-gmaps { background: linear-gradient(135deg,#4285f4,#0f52c0); color:#fff; }
.btn-uber { background: #000; color: #fff; }
.btn-back { background: #f1f5f9; color: #0f172a; border: 2px solid #e2e8f0; }
/* ── ETA Card ── */
.eta-card {
background: linear-gradient(135deg, #0f172a, #1e293b);
color: #fff;
border-radius: 16px;
padding: 16px 20px;
margin: 0 20px 14px;
display: flex; align-items: center; gap: 14px;
}
.eta-icon { font-size: 28px; }
.eta-label { font-size: 12px; opacity: .7; margin-bottom: 2px; }
.eta-value { font-size: 17px; font-weight: 700; }
/* ── Delivery ID ── */
.delivery-id {
margin: 0 20px 14px;
background: #fff;
border-radius: 12px;
padding: 12px 18px;
font-size: 12px;
color: #64748b;
box-shadow: 0 1px 4px rgba(0,0,0,.06);
display: flex; gap: 8px; align-items: center;
}
.delivery-id strong { color: #0f172a; }
/* ── Auto-refresh badge ── */
.refresh-badge {
text-align: center; font-size: 12px; color: #94a3b8;
margin-bottom: 20px;
}
</style>
</t>
<t t-set="body_classname">o_web_client</t>
<!-- ═══ HEADER ═══════════════════════════════════════════════════ -->
<div class="track-header">
<div class="uber-logo">Uber</div>
<div>
<div style="font-size:11px;opacity:.7;text-transform:uppercase;letter-spacing:1px;">Delivery Tracking</div>
<h1>Live Order Status</h1>
</div>
<div class="order-ref"># <t t-esc="order_ref"/></div>
</div>
<!-- ═══ STATUS BANNER ════════════════════════════════════════════ -->
<div t-attf-class="status-banner status-#{status_color}" id="status_banner">
<div class="pulse-dot" t-if="uber_status not in ['delivered','cancelled']"></div>
<span t-esc="status_label"/>
<span style="margin-left:auto;font-size:12px;font-weight:400;opacity:.7;">Auto-refreshes every 30s</span>
</div>
<!-- ═══ ETA ══════════════════════════════════════════════════════ -->
<div class="eta-card" t-if="uber_eta">
<div class="eta-icon">⏱️</div>
<div>
<div class="eta-label">Estimated Delivery</div>
<div class="eta-value" t-esc="uber_eta"/>
</div>
</div>
<!-- ═══ DELIVERY ID ══════════════════════════════════════════════ -->
<div class="delivery-id" t-if="uber_delivery_id">
🔖 <strong>Uber Delivery ID:</strong> <t t-esc="uber_delivery_id"/>
</div>
<!-- ═══ LOCATION CARDS ═══════════════════════════════════════════ -->
<div class="cards-row">
<!-- Pickup: Restaurant -->
<div class="location-card">
<div class="card-icon pickup-icon">🏪</div>
<div class="card-label">📍 Pickup Location</div>
<div class="card-title" t-esc="pickup_name"/>
<div class="card-addr">
<t t-if="pickup_street"><t t-esc="pickup_street"/><br/></t>
<t t-if="pickup_city"><t t-esc="pickup_city"/></t>
<t t-if="pickup_state">, <t t-esc="pickup_state"/></t>
<t t-if="pickup_zip"> <t t-esc="pickup_zip"/></t>
<t t-if="pickup_country"><br/><t t-esc="pickup_country"/></t>
</div>
<div class="card-phone" t-if="pickup_phone">📞 <t t-esc="pickup_phone"/></div>
</div>
<!-- Dropoff: Customer -->
<div class="location-card dropoff">
<div class="card-icon dropoff-icon">🏠</div>
<div class="card-label">🎯 Delivery To</div>
<div class="card-title" t-esc="customer_name"/>
<div class="card-addr">
<t t-if="dropoff_street"><t t-esc="dropoff_street"/><br/></t>
<t t-if="dropoff_city"><t t-esc="dropoff_city"/></t>
<t t-if="dropoff_state">, <t t-esc="dropoff_state"/></t>
<t t-if="dropoff_zip"> <t t-esc="dropoff_zip"/></t>
<t t-if="dropoff_country"><br/><t t-esc="dropoff_country"/></t>
</div>
<div class="card-phone" t-if="customer_phone">📞 <t t-esc="customer_phone"/></div>
</div>
</div>
<!-- ═══ MAP ══════════════════════════════════════════════════════ -->
<div class="map-section">
<iframe
t-att-src="maps_embed_url"
allowfullscreen="1"
loading="lazy"
referrerpolicy="no-referrer-when-downgrade"
title="Delivery Route Map"/>
<!-- Map overlay buttons -->
<div class="map-overlay-btns">
<a t-att-href="maps_directions_url" target="_blank" class="map-btn">
🗺️ Open Full Map
</a>
<a t-if="uber_tracking_url and 'sandbox' not in uber_tracking_url"
t-att-href="uber_tracking_url" target="_blank" class="map-btn uber-btn">
⚡ Uber Live Track
</a>
</div>
</div>
<!-- ═══ ACTION BUTTONS ════════════════════════════════════════════ -->
<div class="action-bar">
<a t-att-href="maps_directions_url" target="_blank" class="btn-full btn-gmaps">
🗺️ Google Maps Directions
</a>
<a t-if="uber_tracking_url and 'sandbox' not in uber_tracking_url"
t-att-href="uber_tracking_url" target="_blank" class="btn-full btn-uber">
⚡ Track on Uber
</a>
<a t-att-href="'/web#id=%s&amp;model=%s.order&amp;view_type=form' % (order_id, model_type)"
class="btn-full btn-back">
← Back to Order
</a>
</div>
<!-- ═══ REFRESH BADGE ════════════════════════════════════════════ -->
<div class="refresh-badge">Status auto-refreshes every 30 seconds</div>
<!-- ═══ AUTO-REFRESH SCRIPT ══════════════════════════════════════ -->
<script>
(function(){
var orderId = <t t-esc="order_id"/>;
var modelType = "<t t-esc="model_type"/>";
var statusColors = {
pending: ["⏳ Waiting for Driver", "warning"],
pickup: ["🚗 Driver at Restaurant", "info"],
delivering: ["🛵 On the Way", "primary"],
delivered: ["✅ Delivered", "success"],
cancelled: ["❌ Cancelled", "danger"],
};
function refreshStatus(){
fetch("/web/dataset/call_kw", {
method: "POST",
headers: {"Content-Type":"application/json"},
body: JSON.stringify({
jsonrpc: "2.0", method: "call", id: 1,
params: {
model: modelType + ".order",
method: "read",
args: [[orderId], ["uber_status","uber_tracking_url","uber_eta"]],
kwargs: {}
}
})
})
.then(r => r.json())
.then(data => {
if(!data.result || !data.result[0]) return;
var rec = data.result[0];
var status = rec.uber_status || "";
var info = statusColors[status] || ["Unknown","secondary"];
var banner = document.getElementById("status_banner");
if(banner){
banner.className = "status-banner status-" + info[1];
banner.querySelector("span").textContent = info[0];
}
if(["delivered","cancelled"].includes(status)){
clearInterval(timer);
}
})
.catch(function(){});
}
// Refresh every 30 seconds
var timer = setInterval(refreshStatus, 30000);
})();
</script>
</t>
</template>
</data>
</odoo>