created space for more settings

This commit is contained in:
2025-05-26 09:46:24 +02:00
parent 026b899db3
commit 86cc6bbff2
8 changed files with 476 additions and 295 deletions

View File

@@ -1,8 +1,16 @@
{% extends "common/base.html" %}
{% from "components/header.html" import header %}
{% from "settings/tabs/colors.html" import colors_tab %}
{% from "settings/tabs/general.html" import general_tab %}
{% from "settings/tabs/security.html" import security_tab %}
{% from "settings/components/reset_colors_modal.html" import reset_colors_modal %}
{% block title %}Settings - DocuPulse{% endblock %}
{% block extra_css %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/settings.css') }}">
{% endblock %}
{% block content %}
{{ header(
title="Settings",
@@ -16,313 +24,52 @@
<div class="row">
<div class="col-12">
<div class="card shadow-sm">
<div class="card-header">
<h5 class="card-title mb-0">Theme Colors</h5>
<div class="card-header bg-white">
<ul class="nav nav-tabs card-header-tabs" id="settingsTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="colors-tab" data-bs-toggle="tab" data-bs-target="#colors" type="button" role="tab" aria-controls="colors" aria-selected="true">
<i class="fas fa-palette me-2"></i>Theme Colors
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="general-tab" data-bs-toggle="tab" data-bs-target="#general" type="button" role="tab" aria-controls="general" aria-selected="false">
<i class="fas fa-sliders me-2"></i>General
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="security-tab" data-bs-toggle="tab" data-bs-target="#security" type="button" role="tab" aria-controls="security" aria-selected="false">
<i class="fas fa-shield-alt me-2"></i>Security
</button>
</li>
</ul>
</div>
<div class="card-body">
<form id="colorSettingsForm" method="POST" action="{{ url_for('main.update_colors') }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<!-- Primary Color Section -->
<div class="mb-5">
<h6 class="mb-3">Primary Color</h6>
<div class="row g-4">
<div class="col-md-4">
<div class="card h-100">
<div class="card-body">
<label class="form-label">Main Color</label>
<div class="d-flex align-items-center gap-3 mb-3">
<input type="color"
class="form-control form-control-color d-none"
id="primaryColor"
name="primary_color"
value="{{ primary_color }}"
data-original-value="{{ primary_color }}">
<button type="button"
class="btn btn-outline-secondary"
onclick="document.getElementById('primaryColor').click()">
<i class="fas fa-palette me-1"></i> Choose Color
</button>
</div>
<small class="text-muted">Used for primary buttons, links, and accents</small>
</div>
</div>
</div>
<div class="col-md-8">
<div class="card h-100">
<div class="card-body">
<label class="form-label">Derived Colors</label>
<div class="d-flex gap-3" id="primaryDerivedColors">
<div class="text-center">
<div class="color-preview p-3 rounded shadow-sm mb-2"
data-color-type="base"
style="background-color: var(--primary-color); width: 80px; height: 80px;">
</div>
<small class="d-block text-muted">Base</small>
</div>
<div class="text-center">
<div class="color-preview p-3 rounded shadow-sm mb-2"
data-color-type="light"
style="background-color: var(--primary-light); width: 80px; height: 80px;">
</div>
<small class="d-block text-muted">Light</small>
</div>
<div class="text-center">
<div class="color-preview p-3 rounded shadow-sm mb-2"
data-color-type="bg-light"
style="background-color: var(--primary-bg-light); width: 80px; height: 80px;">
</div>
<small class="d-block text-muted">Background</small>
</div>
<div class="text-center">
<div class="color-preview p-3 rounded shadow-sm mb-2"
data-color-type="opacity"
style="background-color: var(--primary-opacity-15); width: 80px; height: 80px;">
</div>
<small class="d-block text-muted">Opacity 15%</small>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="tab-content" id="settingsTabsContent">
<!-- Colors Tab -->
<div class="tab-pane fade show active" id="colors" role="tabpanel" aria-labelledby="colors-tab">
{{ colors_tab(primary_color, secondary_color, csrf_token) }}
</div>
<!-- Secondary Color Section -->
<div class="mb-5">
<h6 class="mb-3">Secondary Color</h6>
<div class="row g-4">
<div class="col-md-4">
<div class="card h-100">
<div class="card-body">
<label class="form-label">Main Color</label>
<div class="d-flex align-items-center gap-3 mb-3">
<input type="color"
class="form-control form-control-color d-none"
id="secondaryColor"
name="secondary_color"
value="{{ secondary_color }}"
data-original-value="{{ secondary_color }}">
<button type="button"
class="btn btn-outline-secondary"
onclick="document.getElementById('secondaryColor').click()">
<i class="fas fa-palette me-1"></i> Choose Color
</button>
</div>
<small class="text-muted">Used for secondary elements and highlights</small>
</div>
</div>
</div>
<div class="col-md-8">
<div class="card h-100">
<div class="card-body">
<label class="form-label">Derived Colors</label>
<div class="d-flex gap-3" id="secondaryDerivedColors">
<div class="text-center">
<div class="color-preview p-3 rounded shadow-sm mb-2"
data-color-type="base"
style="background-color: var(--secondary-color); width: 80px; height: 80px;">
</div>
<small class="d-block text-muted">Base</small>
</div>
<div class="text-center">
<div class="color-preview p-3 rounded shadow-sm mb-2"
data-color-type="light"
style="background-color: var(--secondary-light); width: 80px; height: 80px;">
</div>
<small class="d-block text-muted">Light</small>
</div>
<div class="text-center">
<div class="color-preview p-3 rounded shadow-sm mb-2"
data-color-type="opacity"
style="background-color: var(--secondary-opacity-15); width: 80px; height: 80px;">
</div>
<small class="d-block text-muted">Opacity 15%</small>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- General Tab -->
<div class="tab-pane fade" id="general" role="tabpanel" aria-labelledby="general-tab">
{{ general_tab() }}
</div>
<div class="d-flex justify-content-end gap-2">
<button type="button" class="btn btn-secondary" data-bs-toggle="modal" data-bs-target="#resetColorsModal">
<i class="fas fa-undo me-1"></i> Reset to Defaults
</button>
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-1"></i> Save Colors
</button>
<!-- Security Tab -->
<div class="tab-pane fade" id="security" role="tabpanel" aria-labelledby="security-tab">
{{ security_tab() }}
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- Reset Colors Confirmation Modal -->
<div id="resetColorsModal" class="modal fade" tabindex="-1" aria-labelledby="resetColorsModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="resetColorsModalLabel">Reset Colors</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="d-flex align-items-center gap-3 mb-3">
<i class="fas fa-exclamation-triangle text-warning" style="font-size: 2rem;"></i>
<div>
<h6 class="mb-1">Are you sure you want to reset the colors?</h6>
<p class="text-muted mb-0">This will restore the default theme colors.</p>
</div>
</div>
<div class="alert alert-warning">
<i class="fas fa-info-circle me-2"></i>
The primary color will be reset to #16767b and the secondary color to #741b5f.
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<form method="POST" action="{{ url_for('main.reset_colors') }}" class="d-inline">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<button type="submit" class="btn btn-warning">
<i class="fas fa-undo me-1"></i> Reset Colors
</button>
</form>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const primaryColorInput = document.getElementById('primaryColor');
const secondaryColorInput = document.getElementById('secondaryColor');
{{ reset_colors_modal(csrf_token) }}
// Color manipulation functions
function hexToRgb(hex) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}
{% endblock %}
function rgbToHex(r, g, b) {
return '#' + [r, g, b].map(x => {
const hex = x.toString(16);
return hex.length === 1 ? '0' + hex : hex;
}).join('');
}
function lightenColor(color, amount) {
const rgb = hexToRgb(color);
if (!rgb) return color;
return rgbToHex(
Math.min(255, Math.round(rgb.r + (255 - rgb.r) * amount)),
Math.min(255, Math.round(rgb.g + (255 - rgb.g) * amount)),
Math.min(255, Math.round(rgb.b + (255 - rgb.b) * amount))
);
}
function darkenColor(color, amount) {
const rgb = hexToRgb(color);
if (!rgb) return color;
return rgbToHex(
Math.max(0, Math.round(rgb.r * (1 - amount))),
Math.max(0, Math.round(rgb.g * (1 - amount))),
Math.max(0, Math.round(rgb.b * (1 - amount)))
);
}
function updateAllColors(color, isPrimary) {
const prefix = isPrimary ? 'primary' : 'secondary';
// Calculate derived colors
const lightColor = lightenColor(color, 0.15);
const bgLightColor = lightenColor(color, 0.9);
const opacity15 = color + '26'; // 15% opacity in hex
// Calculate chart colors
const chartColors = {
base: color,
light: lightenColor(color, 0.2),
lighter: lightenColor(color, 0.4),
lightest: lightenColor(color, 0.6),
pale: lightenColor(color, 0.8)
};
// Update CSS variables for the entire website
document.documentElement.style.setProperty(`--${prefix}-color`, color);
document.documentElement.style.setProperty(`--${prefix}-light`, lightColor);
document.documentElement.style.setProperty(`--${prefix}-bg-light`, bgLightColor);
document.documentElement.style.setProperty(`--${prefix}-opacity-15`, opacity15);
// Update chart color variables
document.documentElement.style.setProperty(`--chart-${prefix}`, chartColors.base);
document.documentElement.style.setProperty(`--chart-${prefix}-light`, chartColors.light);
document.documentElement.style.setProperty(`--chart-${prefix}-lighter`, chartColors.lighter);
document.documentElement.style.setProperty(`--chart-${prefix}-lightest`, chartColors.lightest);
document.documentElement.style.setProperty(`--chart-${prefix}-pale`, chartColors.pale);
// Update derived colors in the preview section
const derivedColorsSection = document.getElementById(`${prefix}DerivedColors`);
if (derivedColorsSection) {
const previews = derivedColorsSection.querySelectorAll('.color-preview');
previews.forEach(preview => {
const colorType = preview.getAttribute('data-color-type');
switch (colorType) {
case 'base':
preview.style.backgroundColor = color;
break;
case 'light':
preview.style.backgroundColor = lightColor;
break;
case 'bg-light':
preview.style.backgroundColor = bgLightColor;
break;
case 'opacity':
preview.style.backgroundColor = opacity15;
break;
}
});
}
// Update the color input value
if (isPrimary) {
primaryColorInput.value = color;
} else {
secondaryColorInput.value = color;
}
}
// Event listeners for color inputs
primaryColorInput.addEventListener('input', () => {
updateAllColors(primaryColorInput.value, true);
});
secondaryColorInput.addEventListener('input', () => {
updateAllColors(secondaryColorInput.value, false);
});
// Initialize colors from database values
function initializeColors() {
// Reset inputs to original database values
primaryColorInput.value = primaryColorInput.dataset.originalValue;
secondaryColorInput.value = secondaryColorInput.dataset.originalValue;
// Update all color previews
updateAllColors(primaryColorInput.value, true);
updateAllColors(secondaryColorInput.value, false);
}
// Initialize colors when the page loads
initializeColors();
});
</script>
{% block extra_js %}
<script src="{{ url_for('static', filename='js/settings.js') }}"></script>
{% endblock %}