feat: add dedicated Create Account form with field inputs and sub-tabs
All checks were successful
Deploy Beta (NATIVE) / deploy (push) Successful in 23s
All checks were successful
Deploy Beta (NATIVE) / deploy (push) Successful in 23s
This commit is contained in:
parent
ea76459d63
commit
4618bb0c6b
1 changed files with 182 additions and 56 deletions
|
|
@ -900,18 +900,67 @@
|
|||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title">
|
||||
<span>🔐</span> 1. Authentication & Sessions
|
||||
<span>🔐</span> 1. Authentication & User Accounts
|
||||
</div>
|
||||
<span class="card-title-tag">POST /api/auth/*</span>
|
||||
</div>
|
||||
|
||||
<p style="font-size: 0.8rem; color: var(--text-muted);">
|
||||
Test supplier onboarding, JWT token issuance, simulated OTP verification, and session management.
|
||||
</p>
|
||||
<!-- Auth Sub-tabs -->
|
||||
<div class="pill-btn-group" style="width: 100%; display: flex;">
|
||||
<button id="tabBtnRegister" class="pill-btn active" style="flex: 1; text-align: center;" onclick="switchAuthSubTab('register')">
|
||||
📝 Create Account
|
||||
</button>
|
||||
<button id="tabBtnLogin" class="pill-btn" style="flex: 1; text-align: center;" onclick="switchAuthSubTab('login')">
|
||||
🔑 Sign In
|
||||
</button>
|
||||
<button id="tabBtnOtp" class="pill-btn" style="flex: 1; text-align: center;" onclick="switchAuthSubTab('otp')">
|
||||
📱 Verify OTP
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Sub-tab 1: Create Account Form -->
|
||||
<div id="authSubTabRegister">
|
||||
<p style="font-size: 0.8rem; color: var(--text-muted); margin-bottom: 0.75rem;">
|
||||
Create a brand new supplier account. Generates JWT tokens and sends mock verification OTPs.
|
||||
</p>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label>Username / Email</label>
|
||||
<label>Username</label>
|
||||
<input type="text" id="regUsername" value="artisan_seller_01">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Email Address</label>
|
||||
<input type="email" id="regEmail" value="artisan01@example.com">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row" style="margin-top: 0.5rem;">
|
||||
<div class="form-group">
|
||||
<label>Phone Number (10 Digits)</label>
|
||||
<input type="text" id="regPhone" value="9876543210">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Password (Min 8 chars)</label>
|
||||
<input type="password" id="regPassword" value="SecurePassword123!">
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; gap: 0.5rem; margin-top: 0.85rem; flex-wrap: wrap;">
|
||||
<button class="btn btn-success btn-sm" onclick="runCreateAccount()">
|
||||
✨ Create Account
|
||||
</button>
|
||||
<button class="btn btn-secondary btn-sm" onclick="randomizeRegisterFields()">
|
||||
🎲 Randomize Fields
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sub-tab 2: Sign In Form -->
|
||||
<div id="authSubTabLogin" style="display: none;">
|
||||
<p style="font-size: 0.8rem; color: var(--text-muted); margin-bottom: 0.75rem;">
|
||||
Authenticate an existing supplier with username or phone number.
|
||||
</p>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label>Username or Phone</label>
|
||||
<input type="text" id="authUsername" value="demo_seller">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
|
@ -919,20 +968,24 @@
|
|||
<input type="password" id="authPassword" value="super_secure_pass_123">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap;">
|
||||
<div style="display: flex; gap: 0.5rem; margin-top: 0.85rem; flex-wrap: wrap;">
|
||||
<button class="btn btn-primary btn-sm" onclick="runLogin()">
|
||||
🔑 Authenticate (Login)
|
||||
🔑 Sign In
|
||||
</button>
|
||||
<button class="btn btn-secondary btn-sm" onclick="runRegister()">
|
||||
📝 Register New User
|
||||
<button class="btn btn-secondary btn-sm" onclick="fillDemoSeller()">
|
||||
⚡ Fill Demo Seller
|
||||
</button>
|
||||
<button class="btn btn-secondary btn-sm" onclick="runLogout()">
|
||||
<button class="btn btn-danger btn-sm" onclick="runLogout()">
|
||||
🚪 Logout
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="border-top: 1px solid var(--border-color); padding-top: 0.75rem; margin-top: 0.25rem;">
|
||||
<!-- Sub-tab 3: Verify OTP Form -->
|
||||
<div id="authSubTabOtp" style="display: none;">
|
||||
<p style="font-size: 0.8rem; color: var(--text-muted); margin-bottom: 0.75rem;">
|
||||
Validate simulated 2FA verification codes (default mock code: <code>123456</code>).
|
||||
</p>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label>OTP Verification Type</label>
|
||||
|
|
@ -942,14 +995,25 @@
|
|||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>OTP Code (Default: 123456)</label>
|
||||
<label>OTP Code</label>
|
||||
<input type="text" id="otpCode" value="123456">
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-secondary btn-sm" style="margin-top: 0.5rem;" onclick="runVerifyOtp()">
|
||||
<div style="display: flex; gap: 0.5rem; margin-top: 0.85rem;">
|
||||
<button class="btn btn-primary btn-sm" onclick="runVerifyOtp()">
|
||||
✅ Verify OTP Code
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Active Session Info Card -->
|
||||
<div style="border-top: 1px solid var(--border-color); padding-top: 0.65rem; display: flex; align-items: center; justify-content: space-between; font-size: 0.75rem; color: var(--text-muted);">
|
||||
<span>Active: <strong id="sessionUserLabel" style="color: var(--text-main);">Anonymous / Dummy</strong></span>
|
||||
<div style="display: flex; gap: 0.4rem;">
|
||||
<button class="btn btn-secondary btn-sm" style="padding: 2px 7px; font-size: 0.7rem;" onclick="copyToken()">Copy JWT</button>
|
||||
<button class="btn btn-secondary btn-sm" style="padding: 2px 7px; font-size: 0.7rem;" onclick="runLogout()">Logout</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Response Inspector -->
|
||||
<div class="response-inspector" id="authResponseWrap" style="display: none;">
|
||||
|
|
@ -1332,12 +1396,23 @@
|
|||
// Update Header Auth UI
|
||||
function updateAuthUI() {
|
||||
const authBadge = document.getElementById('headerAuthUser');
|
||||
const sessionLabel = document.getElementById('sessionUserLabel');
|
||||
const savedUser = localStorage.getItem('seller_central_active_user') || '';
|
||||
|
||||
if (activeToken) {
|
||||
authBadge.innerText = 'Token Active (Bearer)';
|
||||
authBadge.innerText = savedUser ? `User: ${savedUser}` : 'Token Active (Bearer)';
|
||||
authBadge.style.color = '#34d399';
|
||||
if (sessionLabel) {
|
||||
sessionLabel.innerText = savedUser ? `${savedUser} (Authenticated)` : 'Logged In (Bearer Token)';
|
||||
sessionLabel.style.color = '#34d399';
|
||||
}
|
||||
} else {
|
||||
authBadge.innerText = 'Dummy / Anonymous';
|
||||
authBadge.style.color = '#94a3b8';
|
||||
if (sessionLabel) {
|
||||
sessionLabel.innerText = 'Anonymous / Dummy';
|
||||
sessionLabel.style.color = '#94a3b8';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1582,11 +1657,83 @@
|
|||
}
|
||||
|
||||
// =========================================================================
|
||||
// API TEST SUITE FUNCTIONS
|
||||
// AUTHENTICATION & ACCOUNT CREATION LOGIC
|
||||
// =========================================================================
|
||||
|
||||
function switchAuthSubTab(tab) {
|
||||
document.getElementById('authSubTabRegister').style.display = tab === 'register' ? 'block' : 'none';
|
||||
document.getElementById('authSubTabLogin').style.display = tab === 'login' ? 'block' : 'none';
|
||||
document.getElementById('authSubTabOtp').style.display = tab === 'otp' ? 'block' : 'none';
|
||||
|
||||
document.getElementById('tabBtnRegister').classList.toggle('active', tab === 'register');
|
||||
document.getElementById('tabBtnLogin').classList.toggle('active', tab === 'login');
|
||||
document.getElementById('tabBtnOtp').classList.toggle('active', tab === 'otp');
|
||||
}
|
||||
|
||||
function randomizeRegisterFields() {
|
||||
const rand = Math.floor(1000 + Math.random() * 9000);
|
||||
document.getElementById('regUsername').value = `seller_${rand}`;
|
||||
document.getElementById('regEmail').value = `seller_${rand}@example.com`;
|
||||
document.getElementById('regPhone').value = `9${Math.floor(100000000 + Math.random() * 900000000)}`;
|
||||
document.getElementById('regPassword').value = 'SecurePassword123!';
|
||||
showToast('Generated fresh random credentials!', '🎲');
|
||||
}
|
||||
|
||||
function fillDemoSeller() {
|
||||
document.getElementById('authUsername').value = 'demo_seller';
|
||||
document.getElementById('authPassword').value = 'super_secure_pass_123';
|
||||
showToast('Pre-filled demo seller credentials', '⚡');
|
||||
}
|
||||
|
||||
function copyToken() {
|
||||
if (!activeToken) {
|
||||
showToast('No active JWT token found. Please sign in.', '⚠️');
|
||||
return;
|
||||
}
|
||||
navigator.clipboard.writeText(activeToken);
|
||||
showToast('JWT Access Token copied to clipboard!', '📋');
|
||||
}
|
||||
|
||||
async function runCreateAccount() {
|
||||
const username = document.getElementById('regUsername').value.trim();
|
||||
const email = document.getElementById('regEmail').value.trim();
|
||||
const phone = document.getElementById('regPhone').value.trim();
|
||||
const password = document.getElementById('regPassword').value;
|
||||
|
||||
if (!username || !email || !phone || !password) {
|
||||
alert('All fields (username, email, phone, password) are required to create an account.');
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await apiRequest('/api/auth/register/', 'POST', {
|
||||
username,
|
||||
email,
|
||||
phone,
|
||||
password,
|
||||
confirm_password: password
|
||||
});
|
||||
|
||||
showInspector('authResponseWrap', 'authStatusCode', 'authResponseBody', res);
|
||||
if (res.ok && res.data?.access_token) {
|
||||
activeToken = res.data.access_token;
|
||||
localStorage.setItem('seller_central_access_token', activeToken);
|
||||
localStorage.setItem('seller_central_active_user', username);
|
||||
document.getElementById('authUsername').value = username;
|
||||
updateAuthUI();
|
||||
showToast(`Account created for ${username}! Verification OTPs sent.`, '🎉');
|
||||
|
||||
// Automatically switch to OTP tab and pre-fill default test OTP
|
||||
document.getElementById('otpCode').value = '123456';
|
||||
switchAuthSubTab('otp');
|
||||
fetchLogs();
|
||||
} else {
|
||||
const errMsg = typeof res.data === 'object' ? Object.entries(res.data).map(([k,v]) => `${k}: ${v}`).join(' ') : (res.data || res.statusText);
|
||||
showToast(`Registration failed: ${errMsg}`, '❌');
|
||||
}
|
||||
}
|
||||
|
||||
async function runLogin() {
|
||||
const u = document.getElementById('authUsername').value;
|
||||
const u = document.getElementById('authUsername').value.trim();
|
||||
const p = document.getElementById('authPassword').value;
|
||||
|
||||
const res = await apiRequest('/api/auth/login/', 'POST', {
|
||||
|
|
@ -1597,33 +1744,11 @@
|
|||
showInspector('authResponseWrap', 'authStatusCode', 'authResponseBody', res);
|
||||
if (res.ok && res.data?.access_token) {
|
||||
activeToken = res.data.access_token;
|
||||
const loggedUser = res.data?.user?.username || u;
|
||||
localStorage.setItem('seller_central_access_token', activeToken);
|
||||
localStorage.setItem('seller_central_active_user', loggedUser);
|
||||
updateAuthUI();
|
||||
showToast('Logged in successfully! Token stored.', '🔑');
|
||||
fetchLogs();
|
||||
}
|
||||
}
|
||||
|
||||
async function runRegister() {
|
||||
const rand = Math.floor(1000 + Math.random() * 9000);
|
||||
const username = `seller_${rand}`;
|
||||
const email = `seller_${rand}@example.com`;
|
||||
const phone = `9${Math.floor(100000000 + Math.random() * 900000000)}`;
|
||||
|
||||
const res = await apiRequest('/api/auth/register/', 'POST', {
|
||||
username,
|
||||
email,
|
||||
phone,
|
||||
password: 'Password123!'
|
||||
});
|
||||
|
||||
showInspector('authResponseWrap', 'authStatusCode', 'authResponseBody', res);
|
||||
if (res.ok && res.data?.access_token) {
|
||||
activeToken = res.data.access_token;
|
||||
localStorage.setItem('seller_central_access_token', activeToken);
|
||||
document.getElementById('authUsername').value = username;
|
||||
updateAuthUI();
|
||||
showToast(`Created ${username}! Access token saved.`, '🎉');
|
||||
showToast(`Logged in successfully as ${loggedUser}!`, '🔑');
|
||||
fetchLogs();
|
||||
}
|
||||
}
|
||||
|
|
@ -1632,6 +1757,7 @@
|
|||
const res = await apiRequest('/api/auth/logout/', 'POST');
|
||||
activeToken = '';
|
||||
localStorage.removeItem('seller_central_access_token');
|
||||
localStorage.removeItem('seller_central_active_user');
|
||||
updateAuthUI();
|
||||
showInspector('authResponseWrap', 'authStatusCode', 'authResponseBody', res);
|
||||
showToast('Logged out. Cleared local token.', '🚪');
|
||||
|
|
|
|||
Loading…
Reference in a new issue