ats / index.html
abeea's picture
Update index.html
9a06ee4 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ufone Dashboard Replica</title>
<style>
:root {
--primary: #FF7F00; /* Ufone Orange */
--secondary: #2C8A4B; /* Ufone Green */
--bg: #f4f4f4;
--text: #333;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: var(--bg);
margin: 0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.container {
width: 100%;
max-width: 400px;
background: white;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
overflow: hidden;
position: relative;
}
/* Screens */
.screen {
display: none;
padding: 30px;
flex-direction: column;
gap: 15px;
}
.screen.active {
display: flex;
}
h2 { margin-top: 0; color: var(--secondary); }
input {
padding: 12px;
border: 1px solid #ddd;
border-radius: 8px;
font-size: 16px;
}
button {
padding: 12px;
background-color: var(--secondary);
color: white;
border: none;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
transition: background 0.3s;
}
button:hover { background-color: #1e6636; }
/* Dashboard Specifics */
.user-info {
background: var(--secondary);
color: white;
padding: 20px;
margin: -30px -30px 20px -30px;
text-align: center;
}
.usage-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.card {
background: #fff;
border: 1px solid #eee;
border-radius: 8px;
padding: 15px;
text-align: center;
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
}
.card h3 { margin: 5px 0; font-size: 14px; color: #666; }
.card .value { font-size: 18px; font-weight: bold; color: var(--primary); }
.rewards-list {
margin-top: 20px;
max-height: 200px;
overflow-y: auto;
}
.reward-item {
display: flex;
justify-content: space-between;
padding: 10px;
border-bottom: 1px solid #eee;
font-size: 14px;
}
/* Loader */
.loader {
display: none;
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(255,255,255,0.8);
justify-content: center;
align-items: center;
}
.loader.active { display: flex; }
.spinner {
width: 40px; height: 40px;
border: 4px solid #f3f3f3;
border-top: 4px solid var(--primary);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
.error-msg {
color: red;
font-size: 12px;
text-align: center;
display: none;
}
</style>
</head>
<body>
<div class="container">
<div class="loader" id="loader"><div class="spinner"></div></div>
<!-- Screen 1: Login -->
<div id="login-screen" class="screen active">
<h2>Ufone Login</h2>
<p>Enter your number to generate OTP.</p>
<input type="text" id="phone-input" placeholder="033xxxxxxx" value="03356757673">
<button onclick="generateOTP()">Generate OTP</button>
<div class="error-msg" id="login-error"></div>
</div>
<!-- Screen 2: Verify -->
<div id="verify-screen" class="screen">
<h2>Verify OTP</h2>
<p>Enter the code sent to your phone.</p>
<input type="text" id="otp-input" placeholder="Enter OTP (e.g. 123456)">
<button onclick="verifyOTP()">Verify & Login</button>
<button onclick="showScreen('login-screen')" style="background:#888">Back</button>
<div class="error-msg" id="verify-error"></div>
</div>
<!-- Screen 3: Dashboard -->
<div id="dashboard-screen" class="screen">
<div class="user-info">
<h3 id="user-name">User Name</h3>
<span id="user-number">033xxxxxxx</span><br>
<small id="user-pkg">Package Name</small>
</div>
<h4>Current Usage</h4>
<div class="usage-grid" id="usage-container">
<!-- Usage cards injected via JS -->
</div>
<h4>Spin Rewards Available</h4>
<div class="rewards-list" id="rewards-container">
<!-- Rewards injected via JS -->
</div>
<button onclick="location.reload()" style="margin-top:20px; background:#888">Logout</button>
</div>
</div>
<script>
// --- Configuration ---
const BASE_URL = "https://ufon.fak-official.workers.dev/api";
// Using the device ID from your logs
const DEVICE_ID = "jswjc821k3bdngx05t8";
// --- State ---
let state = {
phoneNumber: "",
token: null,
subToken: null
};
// --- Helper Functions ---
function showLoader(show) {
document.getElementById('loader').classList.toggle('active', show);
}
function showScreen(screenId) {
document.querySelectorAll('.screen').forEach(s => s.classList.remove('active'));
document.getElementById(screenId).classList.add('active');
}
function showError(elementId, msg) {
const el = document.getElementById(elementId);
el.style.display = 'block';
el.innerText = msg;
}
// ⚠️ CRITICAL: The API checks X-Signature.
// Since we don't have the private key, we cannot generate a VALID signature.
// This function generates a dummy signature or passes the one from logs for demo purposes.
function getHeaders(bodyStr) {
const timestamp = Date.now().toString();
// In a real app, you would do: HMAC_SHA256(bodyStr + timestamp, SECRET_KEY)
// Since we don't have the key, requests might fail on the server side.
// I am using a dummy signature here.
const dummySignature = "4c1ab0dec70001a1eaec3d0c0d3124796769e848940acba6ef9ba8c2a2702e78";
const headers = {
"Content-Type": "application/json",
"Accept": "application/json, text/plain, */*",
"X-Timestamp": timestamp,
"X-Signature": dummySignature, // The server might reject this if it validates timestamps
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/143.0.0.0 Safari/537.36"
};
if (state.token) {
headers["Authorization"] = `Bearer ${state.token}`;
}
return headers;
}
// --- API Interactions ---
async function generateOTP() {
const phone = document.getElementById('phone-input').value;
if (!phone) return showError('login-error', 'Enter phone number');
state.phoneNumber = phone;
showLoader(true);
const payload = JSON.stringify({
phoneNumber: state.phoneNumber,
deviceId: DEVICE_ID
});
try {
const res = await fetch(`${BASE_URL}/generate-otp`, {
method: "POST",
headers: getHeaders(payload),
body: payload
});
// Note: If CORS fails, check console.
const data = await res.json();
if (res.ok) {
showScreen('verify-screen');
} else {
showError('login-error', data.responseDesc || "Failed to generate OTP");
}
} catch (e) {
console.error(e);
showError('login-error', "Network Error (Check Console for CORS)");
} finally {
showLoader(false);
}
}
async function verifyOTP() {
const otp = document.getElementById('otp-input').value;
if (!otp) return showError('verify-error', 'Enter OTP');
showLoader(true);
const payload = JSON.stringify({
phoneNumber: state.phoneNumber,
deviceId: DEVICE_ID,
otp: otp
});
try {
const res = await fetch(`${BASE_URL}/verify-otp`, {
method: "POST",
headers: getHeaders(payload),
body: payload
});
const data = await res.json();
if (data.success) {
// Save Tokens
state.token = data.token;
state.subToken = data.subToken;
// Fetch Dashboard Data
await getAllData();
} else {
showError('verify-error', "Invalid OTP");
}
} catch (e) {
console.error(e);
showError('verify-error', "Verification Failed");
} finally {
showLoader(false);
}
}
async function getAllData() {
const payload = JSON.stringify({
phoneNumber: state.phoneNumber,
deviceId: DEVICE_ID,
token: state.token,
subToken: state.subToken
});
try {
const res = await fetch(`${BASE_URL}/get-all-data`, {
method: "POST",
headers: getHeaders(payload),
body: payload
});
const responseJson = await res.json();
if (responseJson.success) {
renderDashboard(responseJson.data);
showScreen('dashboard-screen');
} else {
alert("Failed to load dashboard data");
}
} catch (e) {
console.error(e);
}
}
// --- UI Rendering ---
function renderDashboard(data) {
const dash = data.dashboard;
const rewards = data.spinRewards.model;
// Header
document.getElementById('user-name').innerText = data.customerDetails?.name || "Customer";
document.getElementById('user-number').innerText = state.phoneNumber;
document.getElementById('user-pkg').innerText = dash.packageName;
// Usage Cards
const usageContainer = document.getElementById('usage-container');
usageContainer.innerHTML = '';
dash.cumulativeUsage.forEach(usage => {
const div = document.createElement('div');
div.className = 'card';
div.innerHTML = `
<h3>${usage.title}</h3>
<div class="value">${usage.remaining} ${usage.remainingUnit}</div>
<small>Total: ${usage.total}</small>
`;
usageContainer.appendChild(div);
});
// Rewards
const rewardContainer = document.getElementById('rewards-container');
rewardContainer.innerHTML = '';
rewards.forEach(reward => {
const div = document.createElement('div');
div.className = 'reward-item';
div.innerHTML = `
<span>${reward.value}</span>
<span style="color:green; font-weight:bold">Free</span>
`;
rewardContainer.appendChild(div);
});
}
</script>
</body>
</html>