Update Branding look&feel.
This commit is contained in:
@@ -1,30 +1,40 @@
|
|||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container">
|
<div class="management-container">
|
||||||
<div class="header-section">
|
<div class="management-header">
|
||||||
<h1>🎨 System Administrator - Branding Settings</h1>
|
<h1>🎨 Branding Settings</h1>
|
||||||
<p class="subtitle">Customize the appearance and branding of your TimeTrack instance</p>
|
<div class="management-actions">
|
||||||
<a href="{{ url_for('system_admin_dashboard') }}" class="btn btn-secondary">← Back to Dashboard</a>
|
<a href="{{ url_for('system_admin_dashboard') }}" class="btn btn-secondary">← Back to Dashboard</a>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="subtitle">Customize the appearance and branding of {{ branding.app_name }}</p>
|
||||||
|
|
||||||
<!-- Current Branding Preview -->
|
<!-- Current Branding Preview -->
|
||||||
<div class="stats-section">
|
<div class="management-section">
|
||||||
<h3>👁️ Current Branding Preview</h3>
|
<h2>👁️ Current Branding Preview</h2>
|
||||||
<div class="branding-preview">
|
<div class="management-card branding-preview-card">
|
||||||
<div class="preview-card">
|
<div class="card-header">
|
||||||
<div class="preview-header">
|
<h3>Live Preview</h3>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="preview-demo">
|
||||||
|
<div class="demo-header">
|
||||||
{% if branding.logo_filename %}
|
{% if branding.logo_filename %}
|
||||||
<img src="{{ url_for('static', filename='uploads/branding/' + branding.logo_filename) }}"
|
<img src="{{ url_for('static', filename='uploads/branding/' + branding.logo_filename) }}"
|
||||||
alt="{{ branding.logo_alt_text }}"
|
alt="{{ branding.logo_alt_text }}"
|
||||||
class="preview-logo">
|
class="demo-logo">
|
||||||
|
{% else %}
|
||||||
|
<span class="demo-text-logo">{{ branding.app_name }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<h4>{{ branding.app_name }}</h4>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="preview-content">
|
<div class="demo-content">
|
||||||
<p>This is how your branding will appear to users.</p>
|
<p>Welcome to {{ branding.app_name }}</p>
|
||||||
<div class="preview-button" style="background-color: {{ branding.primary_color }};">
|
<button class="btn btn-primary" style="background-color: {{ branding.primary_color }}; border-color: {{ branding.primary_color }};">
|
||||||
Sample Button
|
Sample Button
|
||||||
|
</button>
|
||||||
|
<a href="#" style="color: {{ branding.primary_color }};">Sample Link</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -32,109 +42,96 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Branding Settings Form -->
|
<!-- Branding Settings Form -->
|
||||||
<div class="settings-section">
|
<div class="management-section">
|
||||||
<h3>🔧 Branding Configuration</h3>
|
<h2>🔧 Branding Configuration</h2>
|
||||||
|
<div class="management-card">
|
||||||
<form method="POST" enctype="multipart/form-data" class="settings-form">
|
<form method="POST" enctype="multipart/form-data" class="settings-form">
|
||||||
<!-- Application Name -->
|
<!-- Application Name -->
|
||||||
<div class="setting-group">
|
<div class="form-section">
|
||||||
<div class="setting-header">
|
<h3>📝 Basic Information</h3>
|
||||||
<h4>Application Name</h4>
|
<div class="form-group">
|
||||||
<p>The name that appears throughout the application</p>
|
<label for="app_name">Application Name</label>
|
||||||
</div>
|
|
||||||
<div class="setting-control">
|
|
||||||
<label for="app_name">Application Name:</label>
|
|
||||||
<input type="text" id="app_name" name="app_name"
|
<input type="text" id="app_name" name="app_name"
|
||||||
value="{{ branding.app_name }}"
|
value="{{ branding.app_name }}"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
placeholder="TimeTrack"
|
placeholder="TimeTrack"
|
||||||
required>
|
required>
|
||||||
<small class="setting-description">
|
<small class="form-text text-muted">
|
||||||
This name will appear in the title, navigation, and throughout the interface.
|
This name will appear in the title, navigation, and throughout the interface.
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Logo Upload -->
|
<div class="form-group">
|
||||||
<div class="setting-group">
|
<label for="logo_alt_text">Logo Alternative Text</label>
|
||||||
<div class="setting-header">
|
|
||||||
<h4>Logo Upload</h4>
|
|
||||||
<p>Upload a custom logo for your application</p>
|
|
||||||
</div>
|
|
||||||
<div class="setting-control">
|
|
||||||
<label for="logo_file">Logo File:</label>
|
|
||||||
<input type="file" id="logo_file" name="logo_file"
|
|
||||||
accept="image/*"
|
|
||||||
class="form-control">
|
|
||||||
{% if branding.logo_filename %}
|
|
||||||
<div class="current-file">
|
|
||||||
<strong>Current logo:</strong>
|
|
||||||
<img src="{{ url_for('static', filename='uploads/branding/' + branding.logo_filename) }}"
|
|
||||||
alt="{{ branding.logo_alt_text }}"
|
|
||||||
class="current-logo">
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
<small class="setting-description">
|
|
||||||
Supported formats: PNG, JPG, GIF, SVG. Recommended size: 200x50px or similar aspect ratio.
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Logo Alt Text -->
|
|
||||||
<div class="setting-group">
|
|
||||||
<div class="setting-header">
|
|
||||||
<h4>Logo Alt Text</h4>
|
|
||||||
<p>Alternative text for the logo (accessibility)</p>
|
|
||||||
</div>
|
|
||||||
<div class="setting-control">
|
|
||||||
<label for="logo_alt_text">Alt Text:</label>
|
|
||||||
<input type="text" id="logo_alt_text" name="logo_alt_text"
|
<input type="text" id="logo_alt_text" name="logo_alt_text"
|
||||||
value="{{ branding.logo_alt_text }}"
|
value="{{ branding.logo_alt_text }}"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
placeholder="Company Logo">
|
placeholder="Company Logo">
|
||||||
<small class="setting-description">
|
<small class="form-text text-muted">
|
||||||
This text will be read by screen readers and shown when the logo cannot be displayed.
|
Text displayed when the logo cannot be loaded (accessibility).
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Favicon Upload -->
|
<!-- Visual Assets -->
|
||||||
<div class="setting-group">
|
<div class="form-section">
|
||||||
<div class="setting-header">
|
<h3>🖼️ Visual Assets</h3>
|
||||||
<h4>Favicon Upload</h4>
|
<div class="form-row">
|
||||||
<p>Upload a custom favicon (browser tab icon)</p>
|
<div class="form-group col-md-6">
|
||||||
|
<label for="logo_file">Logo Image</label>
|
||||||
|
<input type="file" id="logo_file" name="logo_file"
|
||||||
|
accept="image/*"
|
||||||
|
class="form-control-file">
|
||||||
|
{% if branding.logo_filename %}
|
||||||
|
<div class="current-asset">
|
||||||
|
<img src="{{ url_for('static', filename='uploads/branding/' + branding.logo_filename) }}"
|
||||||
|
alt="{{ branding.logo_alt_text }}"
|
||||||
|
class="current-logo">
|
||||||
|
<span class="asset-label">Current logo</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-control">
|
{% endif %}
|
||||||
<label for="favicon_file">Favicon File:</label>
|
<small class="form-text text-muted">
|
||||||
|
PNG, JPG, GIF, SVG. Recommended: 200x50px
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group col-md-6">
|
||||||
|
<label for="favicon_file">Favicon</label>
|
||||||
<input type="file" id="favicon_file" name="favicon_file"
|
<input type="file" id="favicon_file" name="favicon_file"
|
||||||
accept="image/*"
|
accept="image/*"
|
||||||
class="form-control">
|
class="form-control-file">
|
||||||
{% if branding.favicon_filename %}
|
{% if branding.favicon_filename %}
|
||||||
<div class="current-file">
|
<div class="current-asset">
|
||||||
<strong>Current favicon:</strong>
|
|
||||||
<img src="{{ url_for('static', filename='uploads/branding/' + branding.favicon_filename) }}"
|
<img src="{{ url_for('static', filename='uploads/branding/' + branding.favicon_filename) }}"
|
||||||
alt="Current favicon"
|
alt="Current favicon"
|
||||||
class="current-favicon">
|
class="current-favicon">
|
||||||
|
<span class="asset-label">Current favicon</span>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<small class="setting-description">
|
<small class="form-text text-muted">
|
||||||
Supported formats: ICO, PNG (16x16px or 32x32px recommended).
|
ICO, PNG. Recommended: 16x16px or 32x32px
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Primary Color -->
|
|
||||||
<div class="setting-group">
|
|
||||||
<div class="setting-header">
|
|
||||||
<h4>Primary Color</h4>
|
|
||||||
<p>The primary color used throughout the application</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-control">
|
|
||||||
<label for="primary_color">Primary Color:</label>
|
<!-- Theme Settings -->
|
||||||
|
<div class="form-section">
|
||||||
|
<h3>🎨 Theme Settings</h3>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="primary_color">Primary Color</label>
|
||||||
|
<div class="color-picker-wrapper">
|
||||||
<input type="color" id="primary_color" name="primary_color"
|
<input type="color" id="primary_color" name="primary_color"
|
||||||
value="{{ branding.primary_color }}"
|
value="{{ branding.primary_color }}"
|
||||||
class="form-control color-picker">
|
class="form-control color-picker">
|
||||||
<small class="setting-description">
|
<input type="text" value="{{ branding.primary_color }}"
|
||||||
This color will be used for buttons, links, and other UI elements throughout the application.
|
class="form-control color-value"
|
||||||
|
id="primary_color_text"
|
||||||
|
pattern="^#[0-9A-Fa-f]{6}$"
|
||||||
|
placeholder="#007bff">
|
||||||
|
</div>
|
||||||
|
<small class="form-text text-muted">
|
||||||
|
This color will be used for buttons, links, and other UI elements.
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -142,81 +139,173 @@
|
|||||||
<!-- Save Button -->
|
<!-- Save Button -->
|
||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
<button type="submit" class="btn btn-primary">💾 Save Branding Settings</button>
|
<button type="submit" class="btn btn-primary">💾 Save Branding Settings</button>
|
||||||
|
<a href="{{ url_for('system_admin_dashboard') }}" class="btn btn-secondary">Cancel</a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* Branding-specific styles that complement the existing design system */
|
/* Branding-specific styles */
|
||||||
.branding-preview {
|
.branding-preview-card {
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-demo {
|
||||||
background: #f8f9fa;
|
background: #f8f9fa;
|
||||||
border: 1px solid #dee2e6;
|
border-radius: 8px;
|
||||||
border-radius: 5px;
|
padding: 2rem;
|
||||||
padding: 20px;
|
text-align: center;
|
||||||
margin-top: 15px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-card {
|
.demo-header {
|
||||||
background: white;
|
margin-bottom: 1.5rem;
|
||||||
border-radius: 5px;
|
padding-bottom: 1rem;
|
||||||
padding: 20px;
|
border-bottom: 1px solid #dee2e6;
|
||||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
||||||
max-width: 400px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-header {
|
.demo-logo {
|
||||||
display: flex;
|
max-height: 50px;
|
||||||
align-items: center;
|
max-width: 200px;
|
||||||
gap: 15px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
padding-bottom: 15px;
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-logo {
|
|
||||||
max-height: 40px;
|
|
||||||
max-width: 150px;
|
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-button {
|
.demo-text-logo {
|
||||||
color: white;
|
font-size: 1.5rem;
|
||||||
padding: 8px 16px;
|
font-weight: 600;
|
||||||
border-radius: 4px;
|
color: #333;
|
||||||
display: inline-block;
|
|
||||||
margin-top: 10px;
|
|
||||||
font-size: 14px;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.current-file {
|
.demo-content {
|
||||||
margin-top: 10px;
|
margin-top: 1rem;
|
||||||
padding: 10px;
|
}
|
||||||
|
|
||||||
|
.demo-content p {
|
||||||
|
color: #6c757d;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-content .btn {
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Current assets display */
|
||||||
|
.current-asset {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
padding: 0.75rem;
|
||||||
background: #f8f9fa;
|
background: #f8f9fa;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border: 1px solid #dee2e6;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.current-logo {
|
.current-logo {
|
||||||
max-height: 30px;
|
max-height: 40px;
|
||||||
max-width: 100px;
|
max-width: 120px;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
margin-left: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.current-favicon {
|
.current-favicon {
|
||||||
max-height: 16px;
|
width: 32px;
|
||||||
max-width: 16px;
|
height: 32px;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
margin-left: 10px;
|
}
|
||||||
|
|
||||||
|
.asset-label {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Color picker styling */
|
||||||
|
.color-picker-wrapper {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-items: center;
|
||||||
|
max-width: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-picker {
|
.color-picker {
|
||||||
width: 80px !important;
|
width: 60px;
|
||||||
height: 40px;
|
height: 38px;
|
||||||
padding: 0 !important;
|
padding: 0.25rem;
|
||||||
|
border: 1px solid #ced4da;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.color-value {
|
||||||
|
flex: 1;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Form sections */
|
||||||
|
.form-section {
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
padding-bottom: 2rem;
|
||||||
|
border-bottom: 1px solid #dee2e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-section:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-section h3 {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: #495057;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* File input styling */
|
||||||
|
.form-control-file {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.375rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Form row for two-column layout */
|
||||||
|
.form-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-right: -0.5rem;
|
||||||
|
margin-left: -0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row > .col-md-6 {
|
||||||
|
flex: 0 0 50%;
|
||||||
|
max-width: 50%;
|
||||||
|
padding-right: 0.5rem;
|
||||||
|
padding-left: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.form-row > .col-md-6 {
|
||||||
|
flex: 0 0 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sync color inputs */
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Sync color picker with text input
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const colorPicker = document.getElementById('primary_color');
|
||||||
|
const colorText = document.getElementById('primary_color_text');
|
||||||
|
|
||||||
|
colorPicker.addEventListener('input', function() {
|
||||||
|
colorText.value = this.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
colorText.addEventListener('input', function() {
|
||||||
|
if (this.value.match(/^#[0-9A-Fa-f]{6}$/)) {
|
||||||
|
colorPicker.value = this.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user