Files
Job-Info/Mode3/frontend/index.html
T

377 lines
15 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mode3 - QuickBooks Integration</title>
<link rel="stylesheet" href="output.css">
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
margin: 0;
padding: 20px;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
border-radius: 10px;
box-shadow: 0 10px 40px rgba(0,0,0,0.1);
padding: 40px;
}
h1 {
color: #333;
margin: 0 0 10px 0;
}
.subtitle {
color: #666;
margin-bottom: 30px;
}
.card {
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
background: #f9f9f9;
}
.status-label {
display: inline-block;
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: bold;
margin: 5px 0;
}
.status-ok {
background: #d4edda;
color: #155724;
}
.status-error {
background: #f8d7da;
color: #721c24;
}
button {
background: #667eea;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
margin-top: 15px;
}
button:hover {
background: #5568d3;
}
.form-group {
margin: 15px 0;
}
label {
display: block;
margin-bottom: 5px;
font-weight: 500;
color: #333;
}
input[type="text"], input[type="password"] {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 14px;
box-sizing: border-box;
}
.message {
padding: 15px;
border-radius: 5px;
margin-top: 15px;
display: none;
}
.message.success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
display: block;
}
.message.error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
display: block;
}
</style>
</head>
<body>
<div class="container">
<h1>🔌 Mode3 - QuickBooks Integration</h1>
<p class="subtitle">SOAP Server for QuickBooks Web Connector</p>
<div class="card">
<h2>Server Status</h2>
<p>Status: <span id="serverStatus" class="status-label status-ok">✓ Running</span></p>
<p>Mode: <strong>Mode3</strong></p>
<p>Endpoint: <strong>http://localhost:3005/ws</strong></p>
<button onclick="checkStatus()">Refresh Status</button>
</div>
<div class="card">
<h2>QuickBooks Authentication</h2>
<div class="form-group">
<label for="username">Username:</label>
<input type="text" id="username" placeholder="Enter username" value="admin">
</div>
<div class="form-group">
<label for="password">Password:</label>
<input type="password" id="password" placeholder="Enter password" value="password">
</div>
<button onclick="authenticate()">Authenticate with QB</button>
<div id="authMessage" class="message"></div>
</div>
<div class="card">
<h2>Session Info</h2>
<p>Current Session: <span id="sessionStatus" class="status-label status-error">Not authenticated</span></p>
<div id="sessionDetails" style="display: none;">
<p><strong>Company File:</strong> <span id="companyFile"></span></p>
<p><strong>User:</strong> <span id="sessionUser"></span></p>
<p><strong>Expires:</strong> <span id="sessionExpires"></span></p>
<button onclick="logout()">Logout</button>
</div>
</div>
<div class="card">
<h2>Enter Credit Card Charge</h2>
<div class="form-group">
<label for="ccAccount">Credit Card Account:</label>
<input type="text" id="ccAccount" placeholder="e.g., 21010 - Kum&Go Fleet Card" value="21010 - Kum&Go Fleet Card">
</div>
<div class="form-group">
<label for="vendor">Purchased From (Vendor):</label>
<input type="text" id="vendor" placeholder="e.g., Gas Station">
</div>
<div class="form-group">
<label for="chargeDate">Date:</label>
<input type="text" id="chargeDate" placeholder="MM/DD/YYYY">
</div>
<div class="form-group">
<label for="refNum">Ref No:</label>
<input type="text" id="refNum" placeholder="e.g., 123456789">
</div>
<div class="form-group">
<label for="amount">Amount ($):</label>
<input type="text" id="amount" placeholder="e.g., 12.00">
</div>
<div class="form-group">
<label for="expenseAccount">Expense Account:</label>
<input type="text" id="expenseAccount" placeholder="e.g., 60000 - Semi-Variable Expenses:50160 - Gas/Fuel">
</div>
<div class="form-group">
<label for="memo">Memo:</label>
<input type="text" id="memo" placeholder="Optional memo">
</div>
<button onclick="addCharge()">Add Charge</button>
<div id="chargeMessage" class="message"></div>
<button onclick="viewCharges()" style="background: #764ba2;">View Queued Charges</button>
<div id="chargesList" style="display: none; margin-top: 20px;">
<h3>Pending Charges</h3>
<div id="chargesContainer"></div>
</div>
</div>
<div class="card">
<h2>Documentation</h2>
<ul>
<li><strong>SOAP Endpoint:</strong> <code>POST /ws</code> - QuickBooks Web Connector SOAP interface</li>
<li><strong>WSDL:</strong> <code>GET /ws</code> - WSDL definition for QB Web Connector</li>
<li><strong>Status:</strong> <code>GET /quickbooks/status</code> - Check authentication status</li>
<li><strong>Auth:</strong> <code>POST /quickbooks/auth</code> - REST authentication endpoint</li>
<li><strong>Health:</strong> <code>GET /health</code> - Server health check</li>
<li><strong>Charges:</strong> <code>POST /api/charges</code> - Submit credit card charge</li>
<li><strong>Charges List:</strong> <code>GET /api/charges</code> - List all charges</li>
</ul>
</div>
</div>
<script>
// Set today's date as default
function setDefaultDate() {
const today = new Date().toISOString().split('T')[0];
document.getElementById('chargeDate').value = today;
}
async function addCharge() {
const msgDiv = document.getElementById('chargeMessage');
try {
const charge = {
creditCardAccount: document.getElementById('ccAccount').value || '21010 - Kum&Go Fleet Card',
vendor: document.getElementById('vendor').value || 'Unknown Vendor',
amount: document.getElementById('amount').value,
expenseAccount: document.getElementById('expenseAccount').value || '60000 - Semi-Variable Expenses',
date: document.getElementById('chargeDate').value,
refNum: document.getElementById('refNum').value || undefined,
memo: document.getElementById('memo').value || undefined
};
if (!charge.vendor || !charge.amount) {
msgDiv.className = 'message error';
msgDiv.textContent = '✗ Vendor and Amount are required';
return;
}
const res = await fetch('/api/charges', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(charge)
});
const data = await res.json();
if (data.success) {
msgDiv.className = 'message success';
msgDiv.textContent = '✓ ' + data.message + ' (ID: ' + data.charge.id + ')';
// Clear form
document.getElementById('vendor').value = '';
document.getElementById('amount').value = '';
document.getElementById('refNum').value = '';
document.getElementById('memo').value = '';
setDefaultDate();
viewCharges();
} else {
msgDiv.className = 'message error';
msgDiv.textContent = '✗ ' + data.error;
}
} catch (error) {
msgDiv.className = 'message error';
msgDiv.textContent = '✗ Failed to add charge: ' + error.message;
}
}
async function viewCharges() {
const container = document.getElementById('chargesContainer');
try {
const res = await fetch('/api/charges');
const data = await res.json();
let html = `<p><strong>Total Charges: ${data.total}</strong></p>`;
if (data.pending.length > 0) {
html += '<h4>Pending (Not Yet Sent)</h4><ul>';
data.pending.forEach(ch => {
html += `<li>${ch.vendor} - $${ch.amount} on ${ch.date} (${ch.id})</li>`;
});
html += '</ul>';
}
if (data.sent.length > 0) {
html += '<h4>Sent to QB (Awaiting Processing)</h4><ul>';
data.sent.forEach(ch => {
html += `<li>${ch.vendor} - $${ch.amount} on ${ch.date} (${ch.id})</li>`;
});
html += '</ul>';
}
if (data.total === 0) {
html += '<p><em>No charges queued</em></p>';
}
container.innerHTML = html;
document.getElementById('chargesList').style.display = 'block';
} catch (error) {
container.innerHTML = '<p class="error">Failed to load charges</p>';
}
}
async function checkStatus() {
try {
const res = await fetch('/health');
const data = await res.json();
document.getElementById('serverStatus').className = 'status-label status-ok';
document.getElementById('serverStatus').textContent = '✓ Running';
updateSessionInfo();
} catch (error) {
document.getElementById('serverStatus').className = 'status-label status-error';
document.getElementById('serverStatus').textContent = '✗ Offline';
}
}
// ... rest of existing code remains the same ...
// Check status on page load
document.addEventListener('DOMContentLoaded', () => {
setDefaultDate();
checkStatus();
updateSessionInfo();
});
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
const messageDiv = document.getElementById('authMessage');
try {
const res = await fetch('/quickbooks/auth', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
});
const data = await res.json();
if (data.success) {
messageDiv.className = 'message success';
messageDiv.textContent = '✓ ' + data.message;
setTimeout(() => updateSessionInfo(), 500);
} else {
messageDiv.className = 'message error';
messageDiv.textContent = '✗ ' + data.error;
}
} catch (error) {
messageDiv.className = 'message error';
messageDiv.textContent = '✗ Connection failed';
}
}
async function updateSessionInfo() {
try {
const res = await fetch('/quickbooks/status');
const data = await res.json();
if (data.authenticated) {
document.getElementById('sessionStatus').className = 'status-label status-ok';
document.getElementById('sessionStatus').textContent = '✓ Authenticated';
document.getElementById('companyFile').textContent = data.companyFile;
document.getElementById('sessionUser').textContent = data.userName;
document.getElementById('sessionExpires').textContent = data.sessionExpires;
document.getElementById('sessionDetails').style.display = 'block';
} else {
document.getElementById('sessionStatus').className = 'status-label status-error';
document.getElementById('sessionStatus').textContent = '✗ Not authenticated';
document.getElementById('sessionDetails').style.display = 'none';
}
} catch (error) {
console.error('Failed to update session info:', error);
}
}
async function logout() {
try {
const res = await fetch('/quickbooks/logout', { method: 'POST' });
const data = await res.json();
if (data.success) {
updateSessionInfo();
document.getElementById('authMessage').className = 'message success';
document.getElementById('authMessage').textContent = '✓ ' + data.message;
}
} catch (error) {
console.error('Logout failed:', error);
}
}
// Check status on page load
document.addEventListener('DOMContentLoaded', () => {
checkStatus();
updateSessionInfo();
});
</script>
</body>
</html>