Renamed ebook_backend&admin_panel to ebook_backend_admin_panel The & character was being interpreted by shell as background process operator, causing 'Dockerfile not found' errors in Coolify.
1860 lines
41 KiB
HTML
1860 lines
41 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en" xmlns="http://www.w3.org/1999/html">
|
|
<!--
|
|
Admin Panel - Coupon Management System
|
|
-->
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Admin Panel - Coupon Management</title>
|
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
|
<style>
|
|
/*
|
|
Basic reset and foundational styles for consistent rendering across browsers
|
|
*/
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
/*
|
|
Body styling with gradient background and typography settings
|
|
Creates a professional look with Inter font family
|
|
*/
|
|
body {
|
|
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
min-height: 100vh;
|
|
color: #333;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
DASHBOARD LAYOUT
|
|
=====================
|
|
Main container for the dashboard with flexbox layout
|
|
*/
|
|
.dashboard-container {
|
|
display: flex;
|
|
min-height: 100vh;
|
|
}
|
|
|
|
/*
|
|
Fixed header with user information and logout functionality
|
|
Positioned at the top with backdrop blur effect for modern appearance
|
|
*/
|
|
.dashboard-header {
|
|
position: fixed;
|
|
top: 0;
|
|
right: 0;
|
|
left: 280px;
|
|
height: 70px;
|
|
background: rgba(255, 255, 255, 0.95);
|
|
backdrop-filter: blur(20px);
|
|
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
|
|
box-shadow: 0 2px 20px rgba(0, 0, 0, 0.1);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: flex-end;
|
|
padding: 0 30px;
|
|
z-index: 999;
|
|
}
|
|
|
|
/*
|
|
User information display in header
|
|
Shows avatar, name, role with proper spacing
|
|
*/
|
|
.header-user-info {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 15px;
|
|
}
|
|
|
|
/*
|
|
Circular user avatar with gradient background
|
|
Contains user icon with proper centering
|
|
*/
|
|
.user-avatar {
|
|
width: 40px;
|
|
height: 40px;
|
|
background: linear-gradient(135deg, #667eea, #764ba2);
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: white;
|
|
font-weight: 600;
|
|
font-size: 16px;
|
|
}
|
|
|
|
/*
|
|
User details container for name and role
|
|
Stacked vertically with appropriate typography
|
|
*/
|
|
.user-details {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.user-name {
|
|
font-weight: 600;
|
|
font-size: 14px;
|
|
color: #374151;
|
|
}
|
|
|
|
.user-role {
|
|
font-size: 12px;
|
|
color: #6b7280;
|
|
}
|
|
|
|
/*
|
|
Logout button with gradient background and hover effects
|
|
Includes smooth transitions and visual feedback
|
|
*/
|
|
.logout-btn {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 10px 16px;
|
|
background: linear-gradient(135deg, #ef4444, #dc2626);
|
|
color: white;
|
|
border: none;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
transition: all 0.3s ease;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.logout-btn:hover {
|
|
transform: translateY(-1px);
|
|
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);
|
|
background: linear-gradient(135deg, #dc2626, #b91c1c);
|
|
}
|
|
|
|
.logout-btn:active {
|
|
transform: translateY(0);
|
|
}
|
|
|
|
/*
|
|
Fixed sidebar with navigation tabs and header
|
|
*/
|
|
.sidebar {
|
|
width: 280px;
|
|
background: rgba(255, 255, 255, 0.95);
|
|
backdrop-filter: blur(20px);
|
|
border-right: 1px solid rgba(255, 255, 255, 0.2);
|
|
box-shadow: 0 0 40px rgba(0, 0, 0, 0.1);
|
|
position: fixed;
|
|
height: 100vh;
|
|
z-index: 1000;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
/*
|
|
Sidebar header with app branding
|
|
Gradient background matching the theme
|
|
*/
|
|
.sidebar-header {
|
|
padding: 30px 25px;
|
|
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
|
background: linear-gradient(135deg, #667eea, #764ba2);
|
|
color: white;
|
|
}
|
|
|
|
.sidebar-header h2 {
|
|
font-size: 24px;
|
|
font-weight: 700;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.sidebar-header p {
|
|
font-size: 14px;
|
|
opacity: 0.9;
|
|
font-weight: 300;
|
|
}
|
|
|
|
/*
|
|
Navigation container for tabs
|
|
*/
|
|
.sidebar-nav {
|
|
padding: 20px 0;
|
|
}
|
|
|
|
/*
|
|
Individual navigation tabs with hover and active states
|
|
Includes shimmer effect and smooth transitions
|
|
*/
|
|
.tab {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 16px 25px;
|
|
margin: 4px 15px;
|
|
border-radius: 12px;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
font-weight: 500;
|
|
color: #4a5568;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/*
|
|
Shimmer effect for tab hover animation
|
|
Creates a light sweep across the tab
|
|
*/
|
|
.tab:before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: -100%;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.6), transparent);
|
|
transition: left 0.5s;
|
|
}
|
|
|
|
.tab:hover:before {
|
|
left: 100%;
|
|
}
|
|
|
|
.tab:hover {
|
|
background: linear-gradient(135deg, #667eea, #764ba2);
|
|
color: white;
|
|
transform: translateX(5px);
|
|
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
|
|
}
|
|
|
|
.tab.active {
|
|
background: linear-gradient(135deg, #667eea, #764ba2);
|
|
color: white;
|
|
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
|
|
}
|
|
|
|
.tab i {
|
|
margin-right: 15px;
|
|
font-size: 18px;
|
|
width: 20px;
|
|
text-align: center;
|
|
}
|
|
|
|
/*
|
|
Content area with proper spacing and card layout
|
|
*/
|
|
.content {
|
|
margin-left: 280px;
|
|
margin-top: 90px;
|
|
padding: 40px;
|
|
flex: 1;
|
|
background: transparent;
|
|
}
|
|
|
|
/*
|
|
Content cards with glassmorphism effect
|
|
Elevated design with backdrop blur and shadows
|
|
*/
|
|
.content-card {
|
|
background: rgba(255, 255, 255, 0.95);
|
|
backdrop-filter: blur(20px);
|
|
border-radius: 20px;
|
|
padding: 40px;
|
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.1);
|
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
margin-bottom: 30px;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.content-card:hover {
|
|
transform: translateY(-5px);
|
|
box-shadow: 0 30px 80px rgba(0, 0, 0, 0.15);
|
|
}
|
|
|
|
/*
|
|
Content headers with gradient text and proper spacing
|
|
*/
|
|
.content-header {
|
|
margin-bottom: 30px;
|
|
padding-bottom: 20px;
|
|
border-bottom: 2px solid #f1f5f9;
|
|
}
|
|
|
|
.content-header h2 {
|
|
font-size: 32px;
|
|
font-weight: 700;
|
|
background: linear-gradient(135deg, #667eea, #764ba2);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
background-clip: text;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.content-header p {
|
|
color: #64748b;
|
|
font-size: 16px;
|
|
}
|
|
|
|
/*
|
|
Utility class for hiding elements
|
|
*/
|
|
.hidden {
|
|
display: none !important;
|
|
}
|
|
|
|
/*
|
|
Styling for form elements including inputs, radios, and file uploads
|
|
*/
|
|
.form-group {
|
|
margin-bottom: 25px;
|
|
}
|
|
|
|
.form-label {
|
|
display: block;
|
|
margin-bottom: 8px;
|
|
font-weight: 600;
|
|
color: #374151;
|
|
font-size: 14px;
|
|
}
|
|
|
|
/*
|
|
Radio button groups with custom styling
|
|
*/
|
|
.radio-group {
|
|
display: flex;
|
|
gap: 20px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.radio-option {
|
|
display: flex;
|
|
align-items: center;
|
|
cursor: pointer;
|
|
padding: 12px 20px;
|
|
border: 2px solid #e5e7eb;
|
|
border-radius: 12px;
|
|
transition: all 0.3s ease;
|
|
background: #f9fafb;
|
|
}
|
|
|
|
.radio-option:hover {
|
|
border-color: #667eea;
|
|
background: #f0f4ff;
|
|
}
|
|
|
|
.radio-option input[type="radio"] {
|
|
margin-right: 10px;
|
|
transform: scale(1.2);
|
|
accent-color: #667eea;
|
|
}
|
|
|
|
.radio-option.active {
|
|
border-color: #667eea;
|
|
background: linear-gradient(135deg, #667eea, #764ba2);
|
|
color: white;
|
|
}
|
|
|
|
/*
|
|
Standard input styling with focus states
|
|
*/
|
|
input[type="text"], input[type="number"], input[type="file"] {
|
|
width: 100%;
|
|
padding: 15px 20px;
|
|
border: 2px solid #e5e7eb;
|
|
border-radius: 12px;
|
|
font-size: 16px;
|
|
transition: all 0.3s ease;
|
|
background: #f9fafb;
|
|
}
|
|
|
|
input[type="text"]:focus, input[type="number"]:focus, input[type="file"]:focus {
|
|
outline: none;
|
|
border-color: #667eea;
|
|
background: white;
|
|
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
|
}
|
|
|
|
/*
|
|
Drag and drop file upload area with visual feedback
|
|
*/
|
|
.file-upload-area {
|
|
border: 2px dashed #d1d5db;
|
|
border-radius: 12px;
|
|
padding: 40px;
|
|
text-align: center;
|
|
background: #f9fafb;
|
|
transition: all 0.3s ease;
|
|
cursor: pointer;
|
|
position: relative;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.file-upload-area:hover {
|
|
border-color: #667eea;
|
|
background: #f0f4ff;
|
|
}
|
|
|
|
.file-upload-area.dragover {
|
|
border-color: #667eea;
|
|
background: #f0f4ff;
|
|
transform: scale(1.02);
|
|
}
|
|
|
|
.file-upload-icon {
|
|
font-size: 48px;
|
|
color: #9ca3af;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.file-upload-text {
|
|
color: #6b7280;
|
|
font-size: 16px;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.file-upload-hint {
|
|
color: #9ca3af;
|
|
font-size: 14px;
|
|
}
|
|
|
|
/*
|
|
File information display after selection
|
|
*/
|
|
.file-info {
|
|
background: #f0f9ff;
|
|
border: 1px solid #bae6fd;
|
|
border-radius: 8px;
|
|
padding: 12px 16px;
|
|
margin-top: 15px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
|
|
.file-info i {
|
|
color: #0284c7;
|
|
}
|
|
|
|
/*
|
|
Data preview section with statistics and table display
|
|
*/
|
|
.preview-section {
|
|
margin-top: 30px;
|
|
padding-top: 30px;
|
|
border-top: 2px solid #f1f5f9;
|
|
}
|
|
|
|
.preview-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
/*
|
|
Statistics cards for data preview
|
|
*/
|
|
.preview-stats {
|
|
display: flex;
|
|
gap: 20px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.stat-card {
|
|
background: linear-gradient(135deg, #f0f4ff, #e0e7ff);
|
|
border: 1px solid #c7d2fe;
|
|
border-radius: 8px;
|
|
padding: 12px 16px;
|
|
text-align: center;
|
|
min-width: 120px;
|
|
}
|
|
|
|
.stat-number {
|
|
font-size: 24px;
|
|
font-weight: 700;
|
|
color: #3730a3;
|
|
}
|
|
|
|
.stat-label {
|
|
font-size: 12px;
|
|
color: #6b7280;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
}
|
|
|
|
/*
|
|
Comprehensive button styling with variants and states
|
|
*/
|
|
.btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 15px 30px;
|
|
border: none;
|
|
border-radius: 12px;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
text-decoration: none;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/*
|
|
Primary button variant with gradient background
|
|
*/
|
|
.btn-primary {
|
|
background: linear-gradient(135deg, #667eea, #764ba2);
|
|
color: white;
|
|
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
|
|
}
|
|
|
|
.btn-primary:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 12px 35px rgba(102, 126, 234, 0.4);
|
|
}
|
|
|
|
.btn-secondary {
|
|
background: #6b7280;
|
|
color: white;
|
|
}
|
|
|
|
.btn-secondary:hover {
|
|
background: #4b5563;
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.btn-success {
|
|
background: linear-gradient(135deg, #10b981, #059669);
|
|
color: white;
|
|
}
|
|
|
|
.btn-success:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 8px 25px rgba(16, 185, 129, 0.3);
|
|
}
|
|
|
|
.btn-danger {
|
|
background: linear-gradient(135deg, #ef4444, #dc2626);
|
|
color: white;
|
|
}
|
|
|
|
.btn-danger:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 8px 25px rgba(239, 68, 68, 0.3);
|
|
}
|
|
|
|
.btn-sm {
|
|
padding: 8px 16px;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.btn i {
|
|
margin-right: 8px;
|
|
}
|
|
|
|
/*
|
|
Small action buttons for table rows and inline actions
|
|
*/
|
|
.action-buttons {
|
|
display: flex;
|
|
gap: 8px;
|
|
justify-content:left;
|
|
align-items: center;
|
|
}
|
|
|
|
.action-btn {
|
|
padding: 6px 12px;
|
|
border: none;
|
|
border-radius: 6px;
|
|
font-size: 12px;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
min-width: 35px;
|
|
height: 35px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
line-height: 1;
|
|
}
|
|
|
|
.action-btn i {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 100%;
|
|
height: 100%;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.action-btn:hover {
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
.delete-btn {
|
|
background: #ef4444;
|
|
color: white;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.delete-btn:hover {
|
|
background: #dc2626;
|
|
}
|
|
|
|
.delete-btn i {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 100%;
|
|
height: 100%;
|
|
font-size: 14px;
|
|
}
|
|
|
|
/*
|
|
Collapsible section for adding new codes
|
|
*/
|
|
.add-code-section {
|
|
border: 2px solid #00aaff;
|
|
border-radius: 10px;
|
|
padding: 10px;
|
|
background-color: #eaf6ff;
|
|
margin-bottom: 20px;
|
|
}
|
|
.add-code-header {
|
|
font-size: 18px;
|
|
font-weight: bold;
|
|
cursor: pointer;
|
|
color: #0077cc;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
|
|
#add-code-form .add-btn {
|
|
background-color: #1abc9c;
|
|
color: white;
|
|
border: none;
|
|
padding: 8px 12px;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
}
|
|
.hidden {
|
|
display: none;
|
|
}
|
|
|
|
/*
|
|
Search bar with clear button and styling
|
|
*/
|
|
.search-container {
|
|
position: relative;
|
|
margin-bottom: 30px;
|
|
}
|
|
|
|
/*
|
|
Search input with icon spacing and focus states
|
|
*/
|
|
.search-input {
|
|
width: 100%;
|
|
padding: 15px 55px 15px 60px;
|
|
border: 2px solid #e5e7eb;
|
|
border-radius: 12px;
|
|
font-size: 16px;
|
|
transition: all 0.3s ease;
|
|
background: #f9fafb;
|
|
}
|
|
|
|
.search-input:focus {
|
|
outline: none;
|
|
border-color: #667eea;
|
|
background: white;
|
|
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
|
}
|
|
|
|
/*
|
|
Clear search button positioned absolutely
|
|
*/
|
|
.search-clear {
|
|
position: absolute;
|
|
right: 20px;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
background: none;
|
|
border: none;
|
|
color: #6b7280;
|
|
cursor: pointer;
|
|
font-size: 16px;
|
|
display: none;
|
|
transition: color 0.3s ease;
|
|
z-index: 2;
|
|
padding: 5px;
|
|
}
|
|
|
|
.search-clear:hover {
|
|
color: #374151;
|
|
}
|
|
|
|
.search-clear.show {
|
|
display: block;
|
|
}
|
|
|
|
/*
|
|
Status and result display cards with variants
|
|
*/
|
|
.result-card {
|
|
margin-top: 25px;
|
|
padding: 20px;
|
|
border-radius: 12px;
|
|
border-left: 4px solid #667eea;
|
|
background: linear-gradient(135deg, #f0f4ff, #e0e7ff);
|
|
font-weight: 600;
|
|
color: #1e40af;
|
|
}
|
|
|
|
.result-card.success {
|
|
border-left-color: #10b981;
|
|
background: linear-gradient(135deg, #ecfdf5, #d1fae5);
|
|
color: #047857;
|
|
}
|
|
|
|
.result-card.error {
|
|
border-left-color: #ef4444;
|
|
background: linear-gradient(135deg, #fef2f2, #fecaca);
|
|
color: #dc2626;
|
|
}
|
|
|
|
.result-card.warning {
|
|
border-left-color: #f59e0b;
|
|
background: linear-gradient(135deg, #fffbeb, #fef3c7);
|
|
color: #d97706;
|
|
}
|
|
|
|
/*
|
|
Data table with modern styling and hover effects
|
|
*/
|
|
.table-container {
|
|
background: white;
|
|
border-radius: 16px;
|
|
overflow: hidden;
|
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
|
border: 1px solid #e5e7eb;
|
|
}
|
|
|
|
table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
}
|
|
|
|
/*
|
|
Table header with gradient background
|
|
*/
|
|
thead {
|
|
background: linear-gradient(135deg, #667eea, #764ba2);
|
|
color: white;
|
|
}
|
|
|
|
th {
|
|
padding: 20px;
|
|
text-align: left;
|
|
font-weight: 600;
|
|
font-size: 14px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
}
|
|
|
|
/*
|
|
Table body rows with hover effects
|
|
*/
|
|
tbody tr {
|
|
transition: all 0.3s ease;
|
|
border-bottom: 1px solid #f1f5f9;
|
|
}
|
|
|
|
tbody tr:hover {
|
|
background: #f8fafc;
|
|
transform: scale(1.01);
|
|
}
|
|
|
|
tbody tr:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
tbody tr.highlight {
|
|
background: #fef3c7 !important;
|
|
transform: scale(1.02);
|
|
box-shadow: 0 4px 12px rgba(251, 191, 36, 0.3);
|
|
}
|
|
|
|
td {
|
|
padding: 18px 20px;
|
|
font-size: 14px;
|
|
color: #374151;
|
|
}
|
|
|
|
/*
|
|
Center align actions column
|
|
*/
|
|
td:last-child {
|
|
text-align: center;
|
|
}
|
|
|
|
/*
|
|
Modal dialogs with backdrop blur and animations
|
|
*/
|
|
.modal {
|
|
display: none;
|
|
position: fixed;
|
|
z-index: 2000;
|
|
left: 0;
|
|
top: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background-color: rgba(0, 0, 0, 0.6);
|
|
backdrop-filter: blur(5px);
|
|
}
|
|
|
|
.modal.show {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
animation: fadeIn 0.3s ease;
|
|
}
|
|
|
|
/*
|
|
Modal content container with proper styling
|
|
*/
|
|
.modal-content {
|
|
background: white;
|
|
padding: 30px;
|
|
border-radius: 20px;
|
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
|
max-width: 500px;
|
|
width: 90%;
|
|
max-height: 90vh;
|
|
overflow-y: auto;
|
|
position: relative;
|
|
animation: slideUp 0.3s ease;
|
|
}
|
|
|
|
.modal-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 25px;
|
|
padding-bottom: 15px;
|
|
border-bottom: 2px solid #f1f5f9;
|
|
}
|
|
|
|
.modal-header h3 {
|
|
font-size: 24px;
|
|
font-weight: 700;
|
|
background: linear-gradient(135deg, #667eea, #764ba2);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
background-clip: text;
|
|
}
|
|
|
|
.close-btn {
|
|
background: none;
|
|
border: none;
|
|
font-size: 24px;
|
|
cursor: pointer;
|
|
color: #6b7280;
|
|
transition: color 0.3s ease;
|
|
}
|
|
|
|
.close-btn:hover {
|
|
color: #374151;
|
|
}
|
|
|
|
.modal-footer {
|
|
display: flex;
|
|
gap: 15px;
|
|
justify-content: flex-end;
|
|
margin-top: 30px;
|
|
padding-top: 20px;
|
|
border-top: 1px solid #f1f5f9;
|
|
}
|
|
|
|
/*
|
|
Pagination controls with page numbers and navigation
|
|
*/
|
|
.pagination-container {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-top: 30px;
|
|
padding: 20px;
|
|
background: white;
|
|
border-radius: 12px;
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.pagination-info {
|
|
color: #6b7280;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.pagination-controls {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 15px;
|
|
}
|
|
|
|
.page-numbers {
|
|
display: flex;
|
|
gap: 8px;
|
|
}
|
|
|
|
/*
|
|
Individual page number buttons
|
|
*/
|
|
.page-number {
|
|
padding: 8px 12px;
|
|
border: 2px solid #e5e7eb;
|
|
background: white;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
color: #6b7280;
|
|
transition: all 0.3s ease;
|
|
min-width: 40px;
|
|
text-align: center;
|
|
}
|
|
|
|
.page-number:hover {
|
|
border-color: #667eea;
|
|
background: #f0f4ff;
|
|
color: #667eea;
|
|
}
|
|
|
|
.page-number.active {
|
|
background: linear-gradient(135deg, #667eea, #764ba2);
|
|
border-color: #667eea;
|
|
color: white;
|
|
}
|
|
|
|
.page-ellipsis {
|
|
padding: 8px 4px;
|
|
color: #6b7280;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.pagination-controls .btn {
|
|
padding: 8px 16px;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.btn:disabled {
|
|
opacity: 0.5;
|
|
cursor: not-allowed;
|
|
transform: none !important;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.pagination-container {
|
|
flex-direction: column;
|
|
gap: 15px;
|
|
}
|
|
|
|
.page-numbers {
|
|
flex-wrap: wrap;
|
|
justify-content: center;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Status badges for displaying item states
|
|
*/
|
|
.status-badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
padding: 6px 12px;
|
|
border-radius: 20px;
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
}
|
|
|
|
.status-active {
|
|
background: #dcfce7;
|
|
color: #166534;
|
|
}
|
|
|
|
.status-limited {
|
|
background: #fef3c7;
|
|
color: #92400e;
|
|
}
|
|
|
|
/*
|
|
Empty state styling for when no data is available
|
|
*/
|
|
.no-results {
|
|
text-align: center;
|
|
padding: 40px 20px;
|
|
color: #6b7280;
|
|
}
|
|
|
|
.no-results i {
|
|
font-size: 48px;
|
|
margin-bottom: 16px;
|
|
opacity: 0.5;
|
|
}
|
|
|
|
.no-results h3 {
|
|
font-size: 18px;
|
|
margin-bottom: 8px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.no-results p {
|
|
font-size: 14px;
|
|
}
|
|
|
|
/*
|
|
Information displays for search results and validation
|
|
*/
|
|
.search-result-info {
|
|
background: linear-gradient(135deg, #ecfdf5, #d1fae5);
|
|
border: 2px solid #10b981;
|
|
border-radius: 12px;
|
|
padding: 15px 20px;
|
|
margin-bottom: 20px;
|
|
color: #047857;
|
|
font-weight: 600;
|
|
display: none;
|
|
}
|
|
|
|
.search-result-info.show {
|
|
display: block;
|
|
animation: slideIn 0.3s ease-out;
|
|
}
|
|
|
|
.search-result-info i {
|
|
margin-right: 8px;
|
|
color: #10b981;
|
|
}
|
|
|
|
.no-results-info {
|
|
background: linear-gradient(135deg, #fef2f2, #fecaca);
|
|
border: 2px solid #ef4444;
|
|
border-radius: 12px;
|
|
padding: 15px 20px;
|
|
margin-bottom: 20px;
|
|
color: #dc2626;
|
|
font-weight: 600;
|
|
display: none;
|
|
}
|
|
|
|
.no-results-info.show {
|
|
display: block;
|
|
animation: slideIn 0.3s ease-out;
|
|
}
|
|
|
|
.no-results-info i {
|
|
margin-right: 8px;
|
|
color: #ef4444;
|
|
}
|
|
|
|
/*
|
|
Error display for form validation and file upload issues
|
|
*/
|
|
.validation-errors {
|
|
background: linear-gradient(135deg, #fef2f2, #fecaca);
|
|
border: 2px solid #ef4444;
|
|
border-radius: 12px;
|
|
padding: 15px 20px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.validation-errors h4 {
|
|
color: #dc2626;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.validation-errors ul {
|
|
list-style: none;
|
|
padding: 0;
|
|
}
|
|
|
|
.validation-errors li {
|
|
color: #dc2626;
|
|
font-size: 14px;
|
|
margin-bottom: 5px;
|
|
padding-left: 20px;
|
|
position: relative;
|
|
}
|
|
|
|
.validation-errors li:before {
|
|
content: '•';
|
|
position: absolute;
|
|
left: 0;
|
|
font-weight: bold;
|
|
}
|
|
|
|
/*
|
|
Keyframe animations for smooth UI transitions
|
|
*/
|
|
|
|
/*
|
|
Slide in animation for content reveals
|
|
*/
|
|
@keyframes slideIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(20px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
Fade in animation for modal backdrops
|
|
*/
|
|
@keyframes fadeIn {
|
|
from {
|
|
opacity: 0;
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Slide up animation for modal content
|
|
*/
|
|
@keyframes slideUp {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(30px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
Pulse animation for loading states
|
|
*/
|
|
@keyframes pulse {
|
|
0%, 100% {
|
|
opacity: 1;
|
|
}
|
|
50% {
|
|
opacity: 0.7;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Animation utility classes
|
|
*/
|
|
.slide-in {
|
|
animation: slideIn 0.5s ease-out;
|
|
}
|
|
|
|
.loading {
|
|
animation: pulse 1.5s infinite;
|
|
}
|
|
|
|
/*
|
|
Mobile-first responsive design for all screen sizes
|
|
*/
|
|
|
|
/*
|
|
Tablet responsive adjustments
|
|
*/
|
|
@media (max-width: 768px) {
|
|
.sidebar {
|
|
transform: translateX(-100%);
|
|
width: 100%;
|
|
}
|
|
|
|
.content {
|
|
margin-left: 0;
|
|
padding: 20px;
|
|
}
|
|
|
|
.content-card {
|
|
padding: 25px;
|
|
}
|
|
|
|
.add-code-form {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.action-buttons {
|
|
flex-direction: column;
|
|
gap: 5px;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Styled scrollbar for webkit browsers
|
|
*/
|
|
::-webkit-scrollbar {
|
|
width: 8px;
|
|
}
|
|
|
|
::-webkit-scrollbar-track {
|
|
background: #f1f5f9;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb {
|
|
background: linear-gradient(135deg, #667eea, #764ba2);
|
|
border-radius: 4px;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb:hover {
|
|
background: linear-gradient(135deg, #5a67d8, #6b46c1);
|
|
}
|
|
|
|
/*
|
|
Comprehensive responsive design for all device sizes
|
|
*/
|
|
@media (max-width: 1024px) {
|
|
.content {
|
|
margin-left: 280px;
|
|
margin-top: 90px;
|
|
padding: 30px;
|
|
}
|
|
|
|
.content-card {
|
|
padding: 30px;
|
|
}
|
|
|
|
.content-header h2 {
|
|
font-size: 28px;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Tablet screens (768px and below)
|
|
*/
|
|
@media (max-width: 768px) {
|
|
.dashboard-container {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.sidebar {
|
|
width: 100%;
|
|
height: auto;
|
|
position: relative;
|
|
z-index: 1001;
|
|
}
|
|
|
|
.dashboard-header {
|
|
left: 0;
|
|
padding: 0 20px;
|
|
}
|
|
|
|
.content {
|
|
margin-left: 0;
|
|
margin-top: 0;
|
|
padding: 20px;
|
|
}
|
|
|
|
.content-card {
|
|
padding: 20px;
|
|
border-radius: 16px;
|
|
}
|
|
|
|
.content-header h2 {
|
|
font-size: 24px;
|
|
}
|
|
|
|
.content-header p {
|
|
font-size: 14px;
|
|
}
|
|
|
|
/*
|
|
Mobile navigation layout
|
|
*/
|
|
.sidebar-nav {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 10px;
|
|
padding: 15px;
|
|
}
|
|
|
|
.tab {
|
|
flex: 1;
|
|
min-width: 120px;
|
|
margin: 0;
|
|
padding: 12px 15px;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.tab i {
|
|
margin-right: 8px;
|
|
font-size: 16px;
|
|
}
|
|
|
|
/*
|
|
Responsive table handling
|
|
*/
|
|
.table-container {
|
|
overflow-x: auto;
|
|
}
|
|
|
|
#codeTable {
|
|
min-width: 600px;
|
|
}
|
|
|
|
/*
|
|
Responsive form elements
|
|
*/
|
|
.radio-group {
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
}
|
|
|
|
.radio-option {
|
|
padding: 10px 15px;
|
|
}
|
|
|
|
/*
|
|
Responsive search bar
|
|
*/
|
|
.search-container {
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
}
|
|
|
|
.search-input {
|
|
width: 100%;
|
|
}
|
|
|
|
/*
|
|
Responsive pagination
|
|
*/
|
|
.pagination-container {
|
|
flex-direction: column;
|
|
gap: 15px;
|
|
align-items: center;
|
|
}
|
|
|
|
.pagination-controls {
|
|
flex-wrap: wrap;
|
|
justify-content: center;
|
|
}
|
|
|
|
/*
|
|
Responsive file upload
|
|
*/
|
|
.file-upload-area {
|
|
padding: 30px 20px;
|
|
}
|
|
|
|
.file-upload-text {
|
|
font-size: 16px;
|
|
}
|
|
|
|
.file-upload-hint {
|
|
font-size: 12px;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Mobile phones (480px and below)
|
|
*/
|
|
@media (max-width: 480px) {
|
|
.content {
|
|
padding: 15px;
|
|
}
|
|
|
|
.content-card {
|
|
padding: 15px;
|
|
border-radius: 12px;
|
|
}
|
|
|
|
.content-header h2 {
|
|
font-size: 20px;
|
|
}
|
|
|
|
.content-header p {
|
|
font-size: 13px;
|
|
}
|
|
|
|
/*
|
|
Mobile sidebar adjustments
|
|
*/
|
|
.sidebar-header {
|
|
padding: 20px 15px;
|
|
}
|
|
|
|
.sidebar-header h2 {
|
|
font-size: 20px;
|
|
}
|
|
|
|
.sidebar-header p {
|
|
font-size: 12px;
|
|
}
|
|
|
|
.tab {
|
|
padding: 10px 12px;
|
|
font-size: 13px;
|
|
min-width: 100px;
|
|
}
|
|
|
|
.tab i {
|
|
font-size: 14px;
|
|
margin-right: 6px;
|
|
}
|
|
|
|
/*
|
|
Mobile table styling
|
|
*/
|
|
#codeTable {
|
|
min-width: 400px;
|
|
font-size: 12px;
|
|
}
|
|
|
|
#codeTable th,
|
|
#codeTable td {
|
|
padding: 8px 6px;
|
|
}
|
|
|
|
/*
|
|
Mobile form elements
|
|
*/
|
|
input[type="text"],
|
|
input[type="number"],
|
|
input[type="file"] {
|
|
padding: 12px 15px;
|
|
font-size: 16px;
|
|
}
|
|
|
|
.btn {
|
|
padding: 12px 20px;
|
|
font-size: 14px;
|
|
}
|
|
|
|
/*
|
|
Mobile modal adjustments
|
|
*/
|
|
.modal-content {
|
|
margin: 20px;
|
|
padding: 20px;
|
|
max-width: calc(100% - 40px);
|
|
}
|
|
|
|
.modal-header h3 {
|
|
font-size: 18px;
|
|
}
|
|
|
|
.modal-body p {
|
|
font-size: 14px;
|
|
}
|
|
|
|
/*
|
|
Mobile file upload area
|
|
*/
|
|
.file-upload-area {
|
|
padding: 20px 15px;
|
|
}
|
|
|
|
.file-upload-text {
|
|
font-size: 14px;
|
|
}
|
|
|
|
.file-upload-hint {
|
|
font-size: 11px;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Small mobile phones (360px and below)
|
|
*/
|
|
@media (max-width: 360px) {
|
|
.content {
|
|
padding: 10px;
|
|
}
|
|
|
|
.content-card {
|
|
padding: 12px;
|
|
}
|
|
|
|
.content-header h2 {
|
|
font-size: 18px;
|
|
}
|
|
|
|
.tab {
|
|
padding: 8px 10px;
|
|
font-size: 12px;
|
|
min-width: 80px;
|
|
}
|
|
|
|
.tab i {
|
|
font-size: 12px;
|
|
margin-right: 4px;
|
|
}
|
|
|
|
#codeTable {
|
|
min-width: 300px;
|
|
font-size: 11px;
|
|
}
|
|
|
|
.btn {
|
|
padding: 10px 16px;
|
|
font-size: 13px;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Landscape orientation adjustments for mobile devices
|
|
*/
|
|
@media (max-height: 600px) and (orientation: landscape) {
|
|
.sidebar {
|
|
height: auto;
|
|
position: relative;
|
|
}
|
|
|
|
.content {
|
|
margin-top: 0;
|
|
}
|
|
|
|
.sidebar-nav {
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
}
|
|
|
|
.tab {
|
|
padding: 8px 12px;
|
|
font-size: 12px;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<!-- MAIN DASHBOARD STRUCTURE -->
|
|
<div class="dashboard-container">
|
|
<!-- DASHBOARD HEADER -->
|
|
<div class="dashboard-header">
|
|
<div class="header-user-info">
|
|
<!-- User avatar with admin icon -->
|
|
<div class="user-avatar">
|
|
<i class="fas fa-user-shield"></i>
|
|
</div>
|
|
<!-- User details display -->
|
|
<div class="user-details">
|
|
<div class="user-name">Bruno</div>
|
|
<div class="user-role">Administrator</div>
|
|
</div>
|
|
<!-- Logout button with confirmation modal trigger -->
|
|
<button class="logout-btn" onclick="handleLogout()">
|
|
<i class="fas fa-sign-out-alt"></i>
|
|
Logout
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!--
|
|
Fixed sidebar containing app branding and navigation tabs
|
|
-->
|
|
<div class="sidebar">
|
|
<!-- Sidebar header with app branding -->
|
|
<div class="sidebar-header">
|
|
<h2><i class="fas fa-ticket-alt"></i> CouponHub</h2>
|
|
<p>Admin Dashboard</p>
|
|
</div>
|
|
<!-- Navigation tabs for different sections -->
|
|
<nav class="sidebar-nav">
|
|
<div class="tab active" onclick="showTab('generate', event)">
|
|
<i class="fas fa-plus-circle"></i>
|
|
Generate Code
|
|
</div>
|
|
<div class="tab" onclick="showTab('list', event)">
|
|
<i class="fas fa-list-ul"></i>
|
|
List Codes
|
|
</div>
|
|
<div class="tab" onclick="showTab('upload', event)">
|
|
<i class="fas fa-upload"></i>
|
|
Upload Codes
|
|
</div>
|
|
<div class="tab" onclick="showTab('translation-upload', event)">
|
|
<i class="fas fa-language"></i>
|
|
Upload Translation
|
|
</div>
|
|
</nav>
|
|
</div>
|
|
|
|
<!--
|
|
Contains all the different tab contents and functionality
|
|
-->
|
|
<div class="content">
|
|
<!--
|
|
Interface for generating single or bulk coupon codes
|
|
-->
|
|
<div id="generate" class="slide-in">
|
|
<div class="content-card">
|
|
<div class="content-header">
|
|
<h2><i class="fas fa-magic"></i> Generate Coupon Code</h2>
|
|
<p>Create new coupon codes for your campaigns</p>
|
|
</div>
|
|
|
|
<!-- Generation mode selection (single vs bulk) -->
|
|
<div class="form-group">
|
|
<label class="form-label">Generation Mode</label>
|
|
<div class="radio-group">
|
|
<label class="radio-option active">
|
|
<input type="radio" name="genMode" value="single" checked>
|
|
<span><i class="fas fa-ticket-alt"></i> Single Code</span>
|
|
</label>
|
|
<label class="radio-option">
|
|
<input type="radio" name="genMode" value="bulk">
|
|
<span><i class="fas fa-tickets-alt"></i> Bulk Generation</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bulk quantity input (hidden by default) -->
|
|
<div id="bulkCountWrapper" class="form-group hidden">
|
|
<label class="form-label" for="bulkCount">Number of Codes</label>
|
|
<input type="number" id="bulkCount" placeholder="Enter quantity">
|
|
</div>
|
|
|
|
<!-- Generate button -->
|
|
<button class="btn btn-primary" onclick="generateCode()">
|
|
<i class="fas fa-sparkles"></i>
|
|
Generate Codes
|
|
</button>
|
|
|
|
<!-- Result display area -->
|
|
<div id="genResult"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!--
|
|
Interface for viewing, searching, and managing existing coupon codes
|
|
-->
|
|
<div id="list" class="hidden slide-in">
|
|
<div class="content-card">
|
|
<div class="content-header">
|
|
<h2><i class="fas fa-database"></i> Coupon Codes Management</h2>
|
|
<p>Complete list of all coupon codes with edit capabilities</p>
|
|
</div>
|
|
|
|
<!-- Search functionality -->
|
|
<div class="search-container">
|
|
<input type="text" id="searchInput" class="search-input" placeholder="Search coupon codes...">
|
|
<button class="search-clear" id="searchClear" onclick="clearSearch()">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Search result information displays -->
|
|
<div id="searchResultInfo" class="search-result-info">
|
|
<i class="fas fa-check-circle"></i>
|
|
<span id="searchResultText"></span>
|
|
</div>
|
|
|
|
<div id="noResultsInfo" class="no-results-info">
|
|
<i class="fas fa-exclamation-circle"></i>
|
|
<span>No coupon codes found matching your search.</span>
|
|
</div>
|
|
|
|
<!-- Data table for displaying coupon codes -->
|
|
<div class="table-container">
|
|
<table id="codeTable">
|
|
<thead>
|
|
<tr>
|
|
<th><i class="fas fa-tag"></i> Coupon Code</th>
|
|
<th><i class="fas fa-clock"></i> Used At</th>
|
|
<th><i class="fas fa-cog"></i> Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<!-- No results row (hidden by default) -->
|
|
<tr id="noResults" class="no-results" style="display: none;">
|
|
<td colspan="3">
|
|
<i class="fas fa-search"></i>
|
|
<h3>No codes found</h3>
|
|
<p>Try searching for a different coupon code</p>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Pagination controls -->
|
|
<div class="pagination-container">
|
|
<div class="pagination-info">
|
|
<span id="paginationInfo">Showing 0-0 of 0 codes</span>
|
|
</div>
|
|
<div class="pagination-controls">
|
|
<button class="btn btn-secondary" id="prevPage" onclick="changePage(-1)" disabled>
|
|
<i class="fas fa-chevron-left"></i> Previous
|
|
</button>
|
|
<div class="page-numbers" id="pageNumbers"></div>
|
|
<button class="btn btn-secondary" id="nextPage" onclick="changePage(1)" disabled>
|
|
Next <i class="fas fa-chevron-right"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!--
|
|
Interface for bulk uploading coupon codes from Excel files
|
|
-->
|
|
<div id="upload" class="hidden slide-in">
|
|
<div class="content-card">
|
|
<div class="content-header">
|
|
<h2><i class="fas fa-upload"></i> Upload Coupon Codes</h2>
|
|
<p>Bulk upload coupon codes from Excel files (.xlsx, .xls)</p>
|
|
</div>
|
|
<div class="form-group">
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Drag and drop file upload area -->
|
|
<div class="file-upload-area" onclick="document.getElementById('excelFile').click()">
|
|
<i class="fas fa-cloud-upload-alt file-upload-icon"></i>
|
|
<div class="file-upload-text">Click to upload or drag and drop</div>
|
|
<div class="file-upload-hint">Excel files only (.xlsx, .xls) - Max 10MB</div>
|
|
<input type="file" id="excelFile" accept=".xlsx,.xls" style="display: none;" onchange="handleFileSelect(event)">
|
|
</div>
|
|
|
|
<!-- File information display -->
|
|
<div id="fileInfo" class="file-info" style="display: none;">
|
|
<i class="fas fa-file-excel"></i>
|
|
<span id="fileName"></span>
|
|
<button type="button" onclick="clearFile()" style="margin-left: auto; background: none; border: none; color: #ef4444; cursor: pointer;">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Validation error display -->
|
|
<div id="validationErrors" class="validation-errors" style="display: none;">
|
|
<h4><i class="fas fa-exclamation-triangle"></i> Validation Errors</h4>
|
|
<ul id="errorList"></ul>
|
|
</div>
|
|
|
|
<!-- Data preview section -->
|
|
<div id="previewSection" class="preview-section" style="display: none;">
|
|
<div class="preview-header">
|
|
<h3><i class="fas fa-eye"></i> Preview Data</h3>
|
|
<!-- Statistics cards -->
|
|
<div class="preview-stats">
|
|
<div class="stat-card">
|
|
<div class="stat-number" id="totalCodes">0</div>
|
|
<div class="stat-label">Total Codes</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-number" id="validCodes">0</div>
|
|
<div class="stat-label">Valid Codes</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-number" id="duplicateCodes">0</div>
|
|
<div class="stat-label">Duplicates</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Preview data table -->
|
|
<div class="table-container">
|
|
<table id="previewTable">
|
|
<thead>
|
|
<tr>
|
|
<th>Code</th>
|
|
<th>Usage</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="previewTableBody">
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Upload result display -->
|
|
<div id="uploadResult"></div>
|
|
|
|
<!--
|
|
Interface for uploading language translation files
|
|
-->
|
|
<div id="translation-upload" class="hidden slide-in">
|
|
<div class="content-card">
|
|
<div class="content-header">
|
|
<h2><i class="fas fa-language"></i> Upload Language Translation File</h2>
|
|
<p>Upload the Excel file (.xlsx) for language translation used by the extension. Only one file can exist at a time.</p>
|
|
</div>
|
|
<div id="translationUploadSection"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!--
|
|
Confirmation dialogs for delete and logout actions
|
|
-->
|
|
|
|
<!-- Delete confirmation modal -->
|
|
<div id="deleteModal" class="modal">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h3><i class="fas fa-trash"></i> Delete Coupon Code</h3>
|
|
<button class="close-btn" onclick="closeDeleteModal()">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p style="font-size: 16px; color: #374151; margin-bottom: 20px;">
|
|
Are you sure you want to delete the coupon code <strong id="deleteCodeName"></strong>?
|
|
</p>
|
|
<p style="font-size: 14px; color: #ef4444; background: #fef2f2; padding: 15px; border-radius: 8px; border-left: 4px solid #ef4444;">
|
|
<i class="fas fa-exclamation-triangle"></i>
|
|
<strong>Warning:</strong> This action cannot be undone.
|
|
</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-secondary" onclick="closeDeleteModal()">Cancel</button>
|
|
<button class="btn btn-danger" onclick="confirmDeleteCode()">
|
|
<i class="fas fa-trash"></i> Delete Code
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Logout confirmation modal -->
|
|
<div id="logoutModal" class="modal">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h3><i class="fas fa-sign-out-alt"></i> Confirm Logout</h3>
|
|
<button class="close-btn" onclick="closeLogoutModal()">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p style="font-size: 16px; color: #374151; margin-bottom: 20px;">
|
|
Are you sure you want to logout from the admin panel?
|
|
</p>
|
|
<p style="font-size: 14px; color: #6b7280; background: #f9fafb; padding: 15px; border-radius: 8px; border-left: 4px solid #667eea;">
|
|
<i class="fas fa-info-circle"></i>
|
|
You will be redirected to the login page.
|
|
</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-secondary" onclick="closeLogoutModal()">Cancel</button>
|
|
<button class="btn btn-danger" onclick="confirmLogout()">
|
|
<i class="fas fa-sign-out-alt"></i> Logout
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!--
|
|
Load required JavaScript libraries and application scripts
|
|
-->
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
|
|
<script src="/static/admin_dashboard.js"></script>
|
|
</body>
|
|
</html> |