327 lines
15 KiB
HTML
327 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>Admin Panel - Video Optimizer</title>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="style.css">
|
|
<link rel="stylesheet" href="admin.css">
|
|
<!-- MSAL Browser Library for Microsoft SSO -->
|
|
<script src="https://cdn.jsdelivr.net/npm/@azure/msal-browser@3.5.0/lib/msal-browser.min.js"></script>
|
|
</head>
|
|
<body>
|
|
<!-- Login Page (shown when not authenticated) -->
|
|
<div id="loginPage" class="login-page" style="display: none;">
|
|
<div class="login-container">
|
|
<div class="login-card">
|
|
<h1>Admin Panel</h1>
|
|
<p class="subtitle">Platform Specifications Management</p>
|
|
<p class="login-message">Please sign in with your Microsoft account to continue</p>
|
|
<button class="btn-login" id="loginBtn">
|
|
<span>Sign in with Microsoft</span>
|
|
</button>
|
|
<div id="loginError" class="error-message" style="display: none;"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Main Application (shown when authenticated) -->
|
|
<div class="container" id="mainApp" style="display: none;">
|
|
<!-- Auth Header -->
|
|
<div class="auth-header">
|
|
<div class="auth-user-info">
|
|
<span class="user-email" id="userEmail"></span>
|
|
</div>
|
|
<div class="auth-actions">
|
|
<a href="help.html" class="btn-help" title="Help & Documentation">Help</a>
|
|
<a href="index.html" class="btn-home" title="Main Application">Home</a>
|
|
<button class="btn-logout" id="logoutBtn">Logout</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Header -->
|
|
<header class="header">
|
|
<h1>Admin Panel</h1>
|
|
<p class="subtitle">Platform Specifications Management</p>
|
|
<div class="header-actions">
|
|
<a href="index.html" class="btn-link">← Back to App</a>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Metrics Overview -->
|
|
<section class="metrics-section">
|
|
<h2>Current Metrics</h2>
|
|
<div class="metrics-grid">
|
|
<div class="metric-card">
|
|
<div class="metric-value" id="totalPlatforms">0</div>
|
|
<div class="metric-label">Total Platforms</div>
|
|
</div>
|
|
<div class="metric-card">
|
|
<div class="metric-value" id="totalFormats">0</div>
|
|
<div class="metric-label">Total Configurations</div>
|
|
</div>
|
|
<div class="metric-card">
|
|
<div class="metric-value" id="totalCodecs">0</div>
|
|
<div class="metric-label">Unique Codecs</div>
|
|
</div>
|
|
<div class="metric-card">
|
|
<div class="metric-value" id="totalAspectRatios">0</div>
|
|
<div class="metric-label">Aspect Ratios</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Actions Bar -->
|
|
<section class="actions-bar">
|
|
<button class="btn-primary" id="addPlatformBtn">+ Add New Platform</button>
|
|
<button class="btn-secondary" id="exportBtn">Export Specs (JSON)</button>
|
|
<button class="btn-secondary" id="importBtn">Import Specs (JSON)</button>
|
|
<input type="file" id="importFile" accept=".json" hidden>
|
|
<button class="btn-secondary" id="reloadBtn">Refresh Display</button>
|
|
<button class="btn-secondary btn-danger" id="resetFactoryBtn">⚠️ Reset to Factory Defaults</button>
|
|
</section>
|
|
|
|
<!-- Platforms Table -->
|
|
<section class="platforms-section">
|
|
<h2>Platform Specifications</h2>
|
|
<div id="platformsList">
|
|
<!-- Platforms will be dynamically loaded here -->
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Naming Conventions Section -->
|
|
<section class="naming-section">
|
|
<h2>Naming Conventions</h2>
|
|
<p class="section-description">Define filename patterns for automatic platform and aspect ratio detection. Use regular expressions or simple text patterns.</p>
|
|
|
|
<div class="naming-grid">
|
|
<!-- Platform Patterns -->
|
|
<div class="naming-panel">
|
|
<h3>Platform Detection Patterns</h3>
|
|
<p class="hint">Patterns to identify platform from filename (e.g., "_tiktok_", "_meta_")</p>
|
|
<div id="platformPatternsList">
|
|
<!-- Platform patterns will be loaded here -->
|
|
</div>
|
|
<button class="btn-secondary btn-sm" id="addPlatformPatternBtn">+ Add Platform Pattern</button>
|
|
</div>
|
|
|
|
<!-- Aspect Ratio Patterns -->
|
|
<div class="naming-panel">
|
|
<h3>Aspect Ratio Detection Patterns</h3>
|
|
<p class="hint">Patterns to identify aspect ratio from filename (e.g., "_16x9_", "_1x1_")</p>
|
|
<div id="aspectRatioPatternsList">
|
|
<!-- Aspect ratio patterns will be loaded here -->
|
|
</div>
|
|
<button class="btn-secondary btn-sm" id="addAspectRatioPatternBtn">+ Add Aspect Ratio Pattern</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="naming-actions">
|
|
<button class="btn-primary" id="saveNamingBtn">Save Naming Conventions</button>
|
|
<button class="btn-secondary" id="testNamingBtn">Test Pattern</button>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Box Automation Section -->
|
|
<section class="box-section" id="boxSection">
|
|
<h2>Box Automation</h2>
|
|
<p class="section-description">Status and processing history for the Box.com automated pipeline.</p>
|
|
|
|
<!-- Connection Status -->
|
|
<div class="box-status-grid" id="boxStatusGrid">
|
|
<div class="box-status-card" id="boxStatusCard">
|
|
<div class="box-status-indicator" id="boxStatusIndicator"></div>
|
|
<div class="box-status-details">
|
|
<div class="box-status-title" id="boxStatusTitle">Checking...</div>
|
|
<div class="box-status-sub" id="boxStatusSub"></div>
|
|
</div>
|
|
</div>
|
|
<div class="box-status-card">
|
|
<div class="box-stat-label">IN Folder</div>
|
|
<div class="box-stat-value" id="boxFolderIn">—</div>
|
|
</div>
|
|
<div class="box-status-card">
|
|
<div class="box-stat-label">OUT_SUCCESS</div>
|
|
<div class="box-stat-value" id="boxFolderSuccess">—</div>
|
|
</div>
|
|
<div class="box-status-card">
|
|
<div class="box-stat-label">OUT_FAILED</div>
|
|
<div class="box-stat-value" id="boxFolderFailed">—</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- History Controls -->
|
|
<div class="box-history-controls">
|
|
<h3>Processing History</h3>
|
|
<div class="box-filter-row">
|
|
<label for="boxHistoryDate">Date:</label>
|
|
<input type="date" id="boxHistoryDate" class="text-input box-date-input">
|
|
<button class="btn-secondary btn-sm" id="boxRefreshBtn">Refresh</button>
|
|
<button class="btn-secondary btn-sm" id="boxLast7Btn">Last 7 days</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Summary stats -->
|
|
<div class="box-summary-row" id="boxSummaryRow" style="display:none;">
|
|
<span id="boxSummaryText"></span>
|
|
</div>
|
|
|
|
<!-- History Table -->
|
|
<div class="box-history-table-wrap">
|
|
<table class="box-history-table" id="boxHistoryTable">
|
|
<thead>
|
|
<tr>
|
|
<th>Time</th>
|
|
<th>Platform</th>
|
|
<th>Ratio</th>
|
|
<th>Original</th>
|
|
<th>Optimised</th>
|
|
<th>Reduction</th>
|
|
<th>Duration</th>
|
|
<th>Status</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="boxHistoryBody">
|
|
<tr><td colspan="8" class="box-empty">Loading...</td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Add/Edit Platform Modal -->
|
|
<div class="modal" id="platformModal" style="display: none;">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h2 id="modalTitle">Add New Platform</h2>
|
|
<button class="modal-close" id="closeModal">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form id="platformForm">
|
|
<div class="form-group">
|
|
<label for="platformKey">Platform Key *</label>
|
|
<input type="text" id="platformKey" class="text-input" placeholder="e.g., tiktok" required>
|
|
<small class="hint">Lowercase, no spaces (used in API and filenames)</small>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="platformName">Platform Name *</label>
|
|
<input type="text" id="platformName" class="text-input" placeholder="e.g., TikTok" required>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="platformCodec">Video Codec *</label>
|
|
<select id="platformCodec" class="select-input" required>
|
|
<option value="">Select Codec...</option>
|
|
<option value="libx264">H264 (libx264)</option>
|
|
<option value="libx265">H265 (libx265)</option>
|
|
<option value="libvpx-vp9">VP9 (libvpx-vp9)</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="platformContainer">Container Format *</label>
|
|
<select id="platformContainer" class="select-input" required>
|
|
<option value="">Select Container...</option>
|
|
<option value="mp4">MP4</option>
|
|
<option value="webm">WebM</option>
|
|
<option value="mov">MOV</option>
|
|
</select>
|
|
</div>
|
|
|
|
<h3>Format Configurations</h3>
|
|
<div id="formatsContainer">
|
|
<!-- Format configurations will be added here -->
|
|
</div>
|
|
|
|
<button type="button" class="btn-secondary" id="addFormatBtn">+ Add Format Configuration</button>
|
|
|
|
<div class="modal-actions">
|
|
<button type="submit" class="btn-primary">Save Platform</button>
|
|
<button type="button" class="btn-secondary" id="cancelBtn">Cancel</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Footer -->
|
|
<footer class="footer">
|
|
<p>Video Optimizer - Admin Panel</p>
|
|
</footer>
|
|
</div>
|
|
|
|
<script src="config.js"></script>
|
|
<script src="auth.js"></script>
|
|
<script src="toast.js"></script>
|
|
<script src="utils.js"></script>
|
|
<script src="admin.js"></script>
|
|
<script src="admin-enhancements.js"></script>
|
|
<script>
|
|
// Initialize authentication and check login status
|
|
async function initializeApp() {
|
|
try {
|
|
// Fetch Azure AD configuration from backend
|
|
const response = await fetch(`${CONFIG.API_BASE}/config`);
|
|
const config = await response.json();
|
|
|
|
// Initialize MSAL
|
|
await window.initAuth(config);
|
|
|
|
// Check authentication status
|
|
if (window.isAuthenticated()) {
|
|
// User is authenticated - show main app
|
|
const userEmail = window.getUserEmail();
|
|
document.getElementById('userEmail').textContent = userEmail || 'User';
|
|
document.getElementById('mainApp').style.display = 'block';
|
|
document.getElementById('loginPage').style.display = 'none';
|
|
} else {
|
|
// User not authenticated - show login page
|
|
document.getElementById('loginPage').style.display = 'flex';
|
|
document.getElementById('mainApp').style.display = 'none';
|
|
}
|
|
} catch (error) {
|
|
console.error('Initialization error:', error);
|
|
|
|
// Show error on login page
|
|
const loginError = document.getElementById('loginError');
|
|
if (loginError) {
|
|
loginError.textContent = error.message || 'Failed to initialize authentication. Please try again.';
|
|
loginError.style.display = 'block';
|
|
}
|
|
|
|
// Show login page even on error
|
|
document.getElementById('loginPage').style.display = 'flex';
|
|
document.getElementById('mainApp').style.display = 'none';
|
|
}
|
|
}
|
|
|
|
// Login button handler
|
|
document.getElementById('loginBtn').addEventListener('click', async () => {
|
|
try {
|
|
document.getElementById('loginError').style.display = 'none';
|
|
await window.login();
|
|
} catch (error) {
|
|
console.error('Login error:', error);
|
|
const loginError = document.getElementById('loginError');
|
|
loginError.textContent = error.message || 'Login failed. Please try again.';
|
|
loginError.style.display = 'block';
|
|
}
|
|
});
|
|
|
|
// Logout button handler
|
|
document.getElementById('logoutBtn').addEventListener('click', async () => {
|
|
try {
|
|
await window.logout();
|
|
} catch (error) {
|
|
console.error('Logout error:', error);
|
|
}
|
|
});
|
|
|
|
// Initialize app on page load
|
|
document.addEventListener('DOMContentLoaded', initializeApp);
|
|
</script>
|
|
</body>
|
|
</html>
|