dynamic colours in website settings

This commit is contained in:
2025-05-26 08:52:04 +02:00
parent 3fe3037aed
commit 026b899db3
19 changed files with 617 additions and 119 deletions

View File

@@ -10,6 +10,8 @@
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<link href="{{ url_for('static', filename='css/file-grid.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/base.css') }}" rel="stylesheet">
<link rel="stylesheet" href="{{ url_for('static', filename='css/colors.css') }}">
<link rel="stylesheet" href="{{ url_for('main.dynamic_colors') }}">
{% block extra_css %}{% endblock %}
</head>
<body>

View File

@@ -4,7 +4,7 @@
<div class="d-flex justify-content-between align-items-center">
<div>
<h3 class="mb-0">
<i class="fas {{ icon }} me-2" style="color:#16767b;"></i>
<i class="fas {{ icon }} me-2" style="color: var(--primary-color);"></i>
{{ title }}
</h3>
{% if description %}
@@ -16,7 +16,7 @@
{% if button_url == "#" %}
<button id="emptyTrashBtn"
class="btn {{ button_class if button_class else '' }}"
style="{{ 'background-color: #16767b; color: white;' if not button_class else '' }}{{ '; ' + button_style if button_style else '' }}">
style="{{ 'background-color: var(--primary-color); color: white;' if not button_class else '' }}{{ '; ' + button_style if button_style else '' }}">
{% if button_icon %}
<i class="fas {{ button_icon }} me-1"></i>
{% endif %}
@@ -25,7 +25,7 @@
{% else %}
<button onclick="window.location.href='{{ button_url }}'"
class="btn {{ button_class if button_class else '' }}"
style="{{ 'background-color: #16767b; color: white;' if not button_class else '' }}{{ '; ' + button_style if button_style else '' }}">
style="{{ 'background-color: var(--primary-color); color: white;' if not button_class else '' }}{{ '; ' + button_style if button_style else '' }}">
{% if button_icon %}
<i class="fas {{ button_icon }} me-1"></i>
{% endif %}

View File

@@ -16,7 +16,7 @@
{% for type in storage_by_type %}
<div class="d-flex justify-content-between align-items-center mb-2">
<div class="d-flex align-items-center">
<i class="fas fa-file me-2 icon-primary"></i>
<div class="color-indicator me-2" style="width: 12px; height: 12px; border-radius: 50%; background-color: var(--chart-{% if loop.index0 == 0 %}primary{% elif loop.index0 == 1 %}primary-light{% elif loop.index0 == 2 %}primary-lighter{% elif loop.index0 == 3 %}primary-lightest{% elif loop.index0 == 4 %}primary-pale{% elif loop.index0 == 5 %}secondary{% elif loop.index0 == 6 %}secondary-light{% elif loop.index0 == 7 %}secondary-lighter{% elif loop.index0 == 8 %}secondary-lightest{% else %}secondary-pale{% endif %});"></div>
<span class="text-muted">{{ type.extension|upper }}:</span>
</div>
<div class="fw-bold text-primary">{{ format_size(type.total_size) }}</div>

View File

@@ -14,7 +14,7 @@
{% for type in trash_by_type %}
<div class="d-flex justify-content-between align-items-center mb-2">
<div class="d-flex align-items-center">
<i class="fas fa-file me-2 icon-primary"></i>
<div class="color-indicator me-2" style="width: 12px; height: 12px; border-radius: 50%; background-color: var(--chart-{% if loop.index0 == 0 %}primary{% elif loop.index0 == 1 %}primary-light{% elif loop.index0 == 2 %}primary-lighter{% elif loop.index0 == 3 %}primary-lightest{% elif loop.index0 == 4 %}primary-pale{% elif loop.index0 == 5 %}secondary{% elif loop.index0 == 6 %}secondary-light{% elif loop.index0 == 7 %}secondary-lighter{% elif loop.index0 == 8 %}secondary-lightest{% else %}secondary-pale{% endif %});"></div>
<span class="text-muted">{{ type.extension|upper }}:</span>
</div>
<div class="fw-bold text-primary">{{ type.count }}</div>

View File

@@ -41,9 +41,9 @@
<option value="user" {% if request.args.get('role') == 'user' %}selected{% endif %}>User</option>
</select>
<button type="button" id="clearFilters" class="px-4 py-2 rounded-lg text-white font-medium transition-colors duration-200"
style="background-color: #16767b; border: 1px solid #16767b;"
onmouseover="this.style.backgroundColor='#1a8a90'"
onmouseout="this.style.backgroundColor='#16767b'">
style="background-color: var(--primary-color); border: 1px solid var(--primary-color);"
onmouseover="this.style.backgroundColor='var(--primary-light)'"
onmouseout="this.style.backgroundColor='var(--primary-color)'">
Clear
</button>
</div>
@@ -132,14 +132,14 @@
<div class="flex flex-row flex-wrap gap-1.5">
<a href="mailto:{{ user.email }}"
class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-sm no-underline transition-colors duration-200"
style="background-color: rgba(22,118,123,0.08); color: #16767b;">
style="background-color: var(--primary-opacity-8); color: var(--primary-color);">
<i class="fas fa-envelope" style="font-size: 0.85em; opacity: 0.7;"></i>
{{ user.email }}
</a>
{% if user.phone %}
<a href="tel:{{ user.phone }}"
class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-sm no-underline transition-colors duration-200"
style="background-color: rgba(22,118,123,0.08); color: #16767b;">
style="background-color: var(--primary-opacity-8); color: var(--primary-color);">
<i class="fas fa-phone" style="font-size: 0.85em; opacity: 0.7;"></i>
{{ user.phone }}
</a>
@@ -182,7 +182,7 @@
<div class="flex justify-end gap-1.5">
<a href="{{ url_for('contacts.edit_contact', id=user.id) }}"
class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-sm no-underline transition-colors duration-200"
style="background-color: rgba(22,118,123,0.08); color: #16767b;">
style="background-color: var(--primary-opacity-8); color: var(--primary-color);">
<i class="fas fa-edit" style="font-size: 0.85em; opacity: 0.7;"></i>
Edit
</a>

View File

@@ -5,6 +5,7 @@
{% block extra_css %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/dashboard.css') }}">
<link rel="stylesheet" href="{{ url_for('main.dynamic_colors') }}">
{% endblock %}
{% block content %}

View File

@@ -31,28 +31,28 @@
</div>
<div class="d-flex gap-2" id="actionButtonsRow">
{% if current_user.is_admin %}
<a href="{{ url_for('rooms.room_members', room_id=room.id) }}" class="btn btn-outline-primary d-flex align-items-center gap-2" style="border-color:#16767b; color:#16767b;" onmouseover="this.style.backgroundColor='#16767b'; this.style.color='white'" onmouseout="this.style.backgroundColor='transparent'; this.style.color='#16767b'">
<a href="{{ url_for('rooms.room_members', room_id=room.id) }}" class="btn btn-outline-primary d-flex align-items-center gap-2" style="border-color:var(--primary-color); color:var(--primary-color);" onmouseover="this.style.backgroundColor='var(--primary-color)'; this.style.color='white'" onmouseout="this.style.backgroundColor='transparent'; this.style.color='var(--primary-color)'">
<i class="fas fa-users"></i> Manage Members
</a>
{% endif %}
{% if current_user.is_admin or can_upload %}
<button type="button" id="newFolderBtn" class="btn btn-outline-primary d-flex align-items-center gap-2" style="border-color:#16767b; color:#16767b;" onmouseover="this.style.backgroundColor='#16767b'; this.style.color='white'" onmouseout="this.style.backgroundColor='transparent'; this.style.color='#16767b'">
<button type="button" id="newFolderBtn" class="btn btn-outline-primary d-flex align-items-center gap-2" style="border-color:var(--primary-color); color:var(--primary-color);" onmouseover="this.style.backgroundColor='var(--primary-color)'; this.style.color='white'" onmouseout="this.style.backgroundColor='transparent'; this.style.color='var(--primary-color)'">
<i class="fas fa-folder-plus"></i> New Folder
</button>
<form id="uploadForm" enctype="multipart/form-data" class="d-inline">
<input type="file" id="fileInput" name="file" multiple style="display:none;" />
<button type="button" id="uploadBtn" class="btn btn-primary d-flex align-items-center gap-2" style="background-color:#16767b; border:1px solid #16767b;" onmouseover="this.style.backgroundColor='#1a8a90'" onmouseout="this.style.backgroundColor='#16767b'">
<button type="button" id="uploadBtn" class="btn btn-primary d-flex align-items-center gap-2" style="background-color:var(--primary-color); border:1px solid var(--primary-color);" onmouseover="this.style.backgroundColor='var(--primary-light)'" onmouseout="this.style.backgroundColor='var(--primary-color)'">
<i class="fas fa-upload"></i> Upload
</button>
</form>
{% endif %}
{% if current_user.is_admin or can_download %}
<button id="downloadSelectedBtn" class="btn btn-outline-primary btn-sm d-flex align-items-center gap-2" style="display:none; border-color:#16767b; color:#16767b;" onmouseover="this.style.backgroundColor='#16767b'; this.style.color='white'" onmouseout="this.style.backgroundColor='transparent'; this.style.color='#16767b'">
<button id="downloadSelectedBtn" class="btn btn-outline-primary btn-sm d-flex align-items-center gap-2" style="display:none; border-color:var(--primary-color); color:var(--primary-color);" onmouseover="this.style.backgroundColor='var(--primary-color)'; this.style.color='white'" onmouseout="this.style.backgroundColor='transparent'; this.style.color='var(--primary-color)'">
<i class="fas fa-download"></i> Download Selected
</button>
{% endif %}
{% if current_user.is_admin or can_delete %}
<button id="deleteSelectedBtn" class="btn btn-outline-danger btn-sm d-flex align-items-center gap-2" style="display:none; border-color:#b91c1c; color:#b91c1c;" onmouseover="this.style.backgroundColor='#b91c1c'; this.style.color='white'" onmouseout="this.style.backgroundColor='transparent'; this.style.color='#b91c1c'">
<button id="deleteSelectedBtn" class="btn btn-outline-danger btn-sm d-flex align-items-center gap-2" style="display:none; border-color:var(--danger-color); color:var(--danger-color);" onmouseover="this.style.backgroundColor='var(--danger-color)'; this.style.color='white'" onmouseout="this.style.backgroundColor='transparent'; this.style.color='var(--danger-color)'">
<i class="fas fa-trash"></i> Delete Selected
</button>
{% endif %}
@@ -60,8 +60,8 @@
</div>
<div class="card-body">
<div id="uploadProgressContainer" style="display:none; margin-bottom: 1rem; width: 100%;">
<div class="progress" style="height: 1.25rem; background-color: #e6f3f4;">
<div id="uploadProgressBar" class="progress-bar" role="progressbar" style="width:0%; background-color:#16767b; color:#fff;">0%</div>
<div class="progress" style="height: 1.25rem; background-color: var(--primary-bg-light);">
<div id="uploadProgressBar" class="progress-bar" role="progressbar" style="width:0%; background-color:var(--primary-color); color:var(--white);">0%</div>
</div>
<div id="uploadProgressText" class="small text-muted mt-1"></div>
<div id="uploadError" class="alert alert-danger alert-dismissible fade show mt-2" style="display:none;" role="alert">
@@ -101,7 +101,7 @@
</div>
<div class="modal-body">
<div class="d-flex align-items-center gap-3 mb-3">
<i class="fas fa-trash text-danger" style="font-size: 2rem;"></i>
<i class="fas fa-trash" style="font-size: 2rem; color: var(--danger-color);"></i>
<div>
<h6 class="mb-1">Move to Trash</h6>
<p class="text-muted mb-0" id="deleteFileName"></p>
@@ -114,7 +114,7 @@
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger" id="confirmDeleteBtn" style="background-color: #b91c1c; border-color: #b91c1c;">
<button type="button" class="btn btn-danger" id="confirmDeleteBtn" style="background-color: var(--danger-color); border-color: var(--danger-color);">
<i class="fas fa-trash me-1"></i>Move to Trash
</button>
</div>
@@ -135,7 +135,7 @@
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="createFolderBtn" style="background-color: #16767b; border-color: #16767b;">Create</button>
<button type="button" class="btn btn-primary" id="createFolderBtn" style="background-color: var(--primary-color); border-color: var(--primary-color);">Create</button>
</div>
</div>
</div>
@@ -154,7 +154,7 @@
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="confirmRenameBtn" style="background-color: #16767b; border-color: #16767b;">Rename</button>
<button type="button" class="btn btn-primary" id="confirmRenameBtn" style="background-color: var(--primary-color); border-color: var(--primary-color);">Rename</button>
</div>
</div>
</div>
@@ -191,8 +191,8 @@
<div class="modal-footer">
<button type="button" class="btn btn-secondary" id="skipOverwriteBtn">Skip</button>
<button type="button" class="btn btn-secondary" id="skipAllOverwriteBtn">Skip All</button>
<button type="button" class="btn btn-danger" id="confirmOverwriteBtn" style="background-color: #b91c1c; border-color: #b91c1c;">Overwrite</button>
<button type="button" class="btn btn-danger" id="confirmAllOverwriteBtn" style="background-color: #b91c1c; border-color: #b91c1c;">Overwrite All</button>
<button type="button" class="btn btn-danger" id="confirmOverwriteBtn" style="background-color: var(--danger-color); border-color: var(--danger-color);">Overwrite</button>
<button type="button" class="btn btn-danger" id="confirmAllOverwriteBtn" style="background-color: var(--danger-color); border-color: var(--danger-color);">Overwrite All</button>
</div>
</div>
</div>
@@ -217,7 +217,7 @@
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="confirmMoveBtn" style="background-color:#16767b; border:1px solid #16767b;">Move</button>
<button type="button" class="btn btn-primary" id="confirmMoveBtn" style="background-color: var(--primary-color); border-color: var(--primary-color);">Move</button>
</div>
</div>
</div>
@@ -270,22 +270,22 @@
#fileGrid.list-view table {
width: 100%;
border-collapse: collapse;
background: #fff;
background: var(--white);
}
#fileGrid.list-view th, #fileGrid.list-view td {
padding: 0.5rem 1rem;
border-bottom: 1px solid #e9ecef;
border-bottom: 1px solid var(--border-light);
text-align: left;
font-size: 0.95rem;
vertical-align: middle;
}
#fileGrid.list-view th {
background: #f8f9fa;
color: #6c757d;
background: var(--bg-color);
color: var(--text-muted);
font-weight: 500;
}
#fileGrid.list-view tr:hover td {
background-color: rgba(22, 118, 123, 0.08);
background-color: var(--primary-opacity-8);
transition: background 0.15s;
}
#fileGrid.list-view .file-icon {
@@ -308,31 +308,31 @@
display: grid;
grid-template-columns: 40px 2fr 1fr 1fr 1fr;
padding: 0.5rem 1rem;
background-color: #f8f9fa;
border-bottom: 1px solid #e9ecef;
color: #6c757d;
background-color: var(--bg-color);
border-bottom: 1px solid var(--border-light);
color: var(--text-muted);
font-size: 0.875rem;
font-weight: 500;
margin-bottom: 0.5rem;
}
.btn-group.btn-group-sm .btn {
background-color: #fff;
border-color: #e9ecef;
color: #6c757d;
background-color: var(--white);
border-color: var(--border-light);
color: var(--text-muted);
transition: background-color 0.15s, color 0.15s;
}
.btn-group.btn-group-sm .btn.active, .btn-group.btn-group-sm .btn:active {
background-color: #e6f3f4 !important;
color: #16767b !important;
border-color: #16767b !important;
background-color: var(--primary-bg-light) !important;
color: var(--primary-color) !important;
border-color: var(--primary-color) !important;
box-shadow: none;
}
.btn-group.btn-group-sm .btn:focus {
box-shadow: 0 0 0 0.1rem #16767b33;
box-shadow: 0 0 0 0.1rem var(--primary-opacity-20);
}
.btn-group.btn-group-sm .btn:hover:not(.active) {
background-color: #f8f9fa;
color: #16767b;
background-color: var(--bg-color);
color: var(--primary-color);
}
#fileGrid.table-mode {
padding: 0;
@@ -340,22 +340,22 @@
#fileGrid.table-mode table {
width: 100%;
border-collapse: collapse;
background: #fff;
background: var(--white);
}
#fileGrid.table-mode th, #fileGrid.table-mode td {
padding: 0.5rem 1rem;
border-bottom: 1px solid #e9ecef;
border-bottom: 1px solid var(--border-light);
text-align: left;
font-size: 0.95rem;
vertical-align: middle;
}
#fileGrid.table-mode th {
background: #f8f9fa;
color: #6c757d;
background: var(--bg-color);
color: var(--text-muted);
font-weight: 500;
}
#fileGrid.table-mode tr:hover td {
background-color: rgba(22, 118, 123, 0.08);
background-color: var(--primary-opacity-8);
transition: background 0.15s;
}
#fileGrid.table-mode .file-icon {
@@ -374,6 +374,9 @@
font-size: 0.875rem;
margin-left: 0.25rem;
}
#fileGrid.table-mode tr.selected {
background-color: var(--primary-bg-light) !important;
}
/* Disable text selection for file grid and table rows/cards */
#fileGrid, #fileGrid * {
user-select: none;
@@ -490,8 +493,8 @@ function showDetailsModal(idx) {
}
const icon = item.type === 'folder'
? `<i class='fas fa-folder' style='font-size:2.2rem;color:#16767b;'></i>`
: `<i class='fas fa-file-alt' style='font-size:2.2rem;color:#741b5f;'></i>`;
? `<i class='fas fa-folder' style='font-size:2.2rem;color:var(--primary-color);'></i>`
: `<i class='fas fa-file-alt' style='font-size:2.2rem;color:var(--secondary-color);'></i>`;
const uploaderPic = item.uploader_profile_pic
? `/uploads/profile_pics/${item.uploader_profile_pic}`
: '/static/default-avatar.png';
@@ -513,11 +516,11 @@ function showDetailsModal(idx) {
<span class='fw-semibold' style='font-size:0.98rem;'>${item.uploaded_by || '-'}</span>
</div>
<div class='mb-2 text-muted' style='font-size:0.92rem;'><i class='far fa-clock me-1'></i>${formatDate(item.modified)}</div>
<hr style='margin:0.7rem 0 0.5rem 0; border-color:#e6f3f4;'>
<div style='font-size:0.91rem;color:#555;'><strong style='color:#16767b;'>Room:</strong> <button class='btn btn-sm' style='background-color:rgba(22,118,123,0.08);color:#16767b;' onclick='window.location.href=\"/room/${roomId}\"'><i class='fas fa-door-open me-1'></i>${roomName}</button></div>
<div style='font-size:0.91rem;color:#555;'><strong style='color:#16767b;'>Path:</strong> <span style='word-break:break-all;'>${(item.path ? item.path + '/' : '') + item.name}</span></div>
<div style='font-size:0.91rem;color:#555;'><strong style='color:#16767b;'>Size:</strong> ${item.size === '-' ? '-' : (item.size > 0 ? (item.size < 1024*1024 ? (item.size/1024).toFixed(1)+' KB' : (item.size/1024/1024).toFixed(2)+' MB') : '0 KB')}</div>
<div style='font-size:0.91rem;color:#555;'><strong style='color:#16767b;'>Uploaded at:</strong> ${item.uploaded_at ? new Date(item.uploaded_at).toLocaleString() : '-'}</div>
<hr style='margin:0.7rem 0 0.5rem 0; border-color:var(--border-light);'>
<div style='font-size:0.91rem;color:var(--text-muted);'><strong style='color:var(--primary-color);'>Room:</strong> <button class='btn btn-sm' style='background-color:var(--primary-opacity-8);color:var(--primary-color);' onclick='window.location.href=\"/room/${roomId}\"'><i class='fas fa-door-open me-1'></i>${roomName}</button></div>
<div style='font-size:0.91rem;color:var(--text-muted);'><strong style='color:var(--primary-color);'>Path:</strong> <span style='word-break:break-all;'>${(item.path ? item.path + '/' : '') + item.name}</span></div>
<div style='font-size:0.91rem;color:var(--text-muted);'><strong style='color:var(--primary-color);'>Size:</strong> ${item.size === '-' ? '-' : (item.size > 0 ? (item.size < 1024*1024 ? (item.size/1024).toFixed(1)+' KB' : (item.size/1024/1024).toFixed(2)+' MB') : '0 KB')}</div>
<div style='font-size:0.91rem;color:var(--text-muted);'><strong style='color:var(--primary-color);'>Uploaded at:</strong> ${item.uploaded_at ? new Date(item.uploaded_at).toLocaleString() : '-'}</div>
`;
document.getElementById('detailsModalBody').innerHTML = detailsHtml;
var modal = new bootstrap.Modal(document.getElementById('detailsModal'));
@@ -589,10 +592,10 @@ function renderBreadcrumb() {
bc.innerHTML = '';
const parts = currentPath ? currentPath.split('/') : [];
let pathSoFar = '';
bc.innerHTML += `<a href="#" onclick="navigateTo('')" class="text-decoration-none" style="color:#16767b;">Root</a>`;
bc.innerHTML += `<a href="#" onclick="navigateTo('')" class="text-decoration-none" style="color:var(--primary-color);">Root</a>`;
parts.forEach((part, idx) => {
pathSoFar += (pathSoFar ? '/' : '') + part;
bc.innerHTML += ` <span class="text-muted">/</span> <a href="#" onclick="navigateTo('${pathSoFar}')" class="text-decoration-none" style="color:#16767b;">${part}</a>`;
bc.innerHTML += ` <span class="text-muted">/</span> <a href="#" onclick="navigateTo('${pathSoFar}')" class="text-decoration-none" style="color:var(--primary-color);">${part}</a>`;
});
// Show/hide up button
const upBtn = document.getElementById('upBtn');
@@ -723,8 +726,8 @@ function renderFiles(files) {
</tr></thead><tbody>`;
filesToRender.forEach((file, idx) => {
let icon = file.type === 'folder'
? `<i class='fas fa-folder' style='font-size:1.5rem;color:#16767b;'></i>`
: `<i class='fas fa-file-alt' style='font-size:1.5rem;color:#741b5f;'></i>`;
? `<i class='fas fa-folder' style='font-size:1.5rem;color:var(--primary-color);'></i>`
: `<i class='fas fa-file-alt' style='font-size:1.5rem;color:var(--secondary-color);'></i>`;
let size = file.size !== '-' ? (file.size > 0 ? (file.size < 1024*1024 ? (file.size/1024).toFixed(1)+' KB' : (file.size/1024/1024).toFixed(2)+' MB') : '0 KB') : '-';
let actionsArr = [];
const canRenameAction = (canRename === 'true');
@@ -737,28 +740,28 @@ function renderFiles(files) {
}
if (file.type === 'file') {
if (canDownload === 'true') {
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Download' style='background-color:rgba(22,118,123,0.08);color:#16767b;' onclick='downloadFile("${file.name}")'><i class='fas fa-download'></i></button>`);
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Download' style='background-color:var(--primary-opacity-8);color:var(--primary-color);' onclick='downloadFile("${file.name}")'><i class='fas fa-download'></i></button>`);
}
if (canRenameAction) {
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Rename' style='background-color:rgba(22,118,123,0.08);color:#16767b;' onclick='showRenameModal("${file.name}")'><i class='fas fa-pen'></i></button>`);
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Rename' style='background-color:var(--primary-opacity-8);color:var(--primary-color);' onclick='showRenameModal("${file.name}")'><i class='fas fa-pen'></i></button>`);
}
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Details' style='background-color:rgba(22,118,123,0.08);color:#16767b;' onclick='showDetailsModal(${idx})'><i class='fas fa-info-circle'></i></button>`);
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Details' style='background-color:var(--primary-opacity-8);color:var(--primary-color);' onclick='showDetailsModal(${idx})'><i class='fas fa-info-circle'></i></button>`);
if (canMove === 'true') {
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Move' style='background-color:rgba(22,118,123,0.08);color:#16767b;' onclick='showMoveModal("${file.name}", "${file.path}")'><i class='fas fa-arrows-alt'></i></button>`);
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Move' style='background-color:var(--primary-opacity-8);color:var(--primary-color);' onclick='showMoveModal("${file.name}", "${file.path}")'><i class='fas fa-arrows-alt'></i></button>`);
}
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='${file.starred ? 'Unstar' : 'Star'}' style='background-color:${file.starred ? 'rgba(255,215,0,0.15)' : 'rgba(22,118,123,0.08)'};color:${file.starred ? '#ffd700' : '#16767b'};' onclick='event.stopPropagation(); toggleStar("${file.name}", "${file.path || ''}")'><i class='fas fa-star'></i></button>`);
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='${file.starred ? 'Unstar' : 'Star'}' style='background-color:${file.starred ? 'var(--warning-opacity-15)' : 'var(--primary-opacity-8)'};color:${file.starred ? 'var(--warning-color)' : 'var(--primary-color)'};' onclick='event.stopPropagation(); toggleStar("${file.name}", "${file.path || ''}")'><i class='fas fa-star'></i></button>`);
if (canDelete === true || canDelete === 'true') {
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Delete' style='background-color:rgba(239,68,68,0.1);color:#b91c1c;' onclick='showDeleteModal("${file.name}", "${file.path}")'><i class='fas fa-trash'></i></button>`);
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Delete' style='background-color:var(--danger-opacity-15);color:var(--danger-color);' onclick='showDeleteModal("${file.name}", "${file.path}")'><i class='fas fa-trash'></i></button>`);
}
} else {
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Open Folder' style='background-color:rgba(22,118,123,0.08);color:#16767b;' onclick='navigateTo(\"${currentPath ? currentPath + '/' : ''}${file.name}\")'><i class='fas fa-folder-open'></i></button>`);
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Open Folder' style='background-color:var(--primary-opacity-8);color:var(--primary-color);' onclick='navigateTo(\"${currentPath ? currentPath + '/' : ''}${file.name}\")'><i class='fas fa-folder-open'></i></button>`);
if (canRenameAction) {
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Rename' style='background-color:rgba(22,118,123,0.08);color:#16767b;' onclick='showRenameModal(\"${file.name}\")'><i class='fas fa-pen'></i></button>`);
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Rename' style='background-color:var(--primary-opacity-8);color:var(--primary-color);' onclick='showRenameModal(\"${file.name}\")'><i class='fas fa-pen'></i></button>`);
}
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Details' style='background-color:rgba(22,118,123,0.08);color:#16767b;' onclick='showDetailsModal(${idx})'><i class='fas fa-info-circle'></i></button>`);
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='${file.starred ? 'Unstar' : 'Star'}' style='background-color:${file.starred ? 'rgba(255,215,0,0.15)' : 'rgba(22,118,123,0.08)'};color:${file.starred ? '#ffd700' : '#16767b'};' onclick='event.stopPropagation(); toggleStar("${file.name}", "${file.path || ''}")'><i class='fas fa-star'></i></button>`);
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Details' style='background-color:var(--primary-opacity-8);color:var(--primary-color);' onclick='showDetailsModal(${idx})'><i class='fas fa-info-circle'></i></button>`);
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='${file.starred ? 'Unstar' : 'Star'}' style='background-color:${file.starred ? 'var(--warning-opacity-15)' : 'var(--primary-opacity-8)'};color:${file.starred ? 'var(--warning-color)' : 'var(--primary-color)'};' onclick='event.stopPropagation(); toggleStar("${file.name}", "${file.path || ''}")'><i class='fas fa-star'></i></button>`);
if (canDelete === true || canDelete === 'true') {
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Delete' style='background-color:rgba(239,68,68,0.1);color:#b91c1c;' onclick='showDeleteModal("${file.name}", "${file.path}")'><i class='fas fa-trash'></i></button>`);
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Delete' style='background-color:var(--danger-opacity-15);color:var(--danger-color);' onclick='showDeleteModal("${file.name}", "${file.path}")'><i class='fas fa-trash'></i></button>`);
}
}
// Move Delete to the end if present
@@ -806,28 +809,28 @@ function renderFiles(files) {
}
if (file.type === 'file') {
if (canDownload === 'true') {
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Download' style='background-color:rgba(22,118,123,0.08);color:#16767b;' onclick='downloadFile("${file.name}")'><i class='fas fa-download'></i></button>`);
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Download' style='background-color:var(--primary-opacity-8);color:var(--primary-color);' onclick='downloadFile("${file.name}")'><i class='fas fa-download'></i></button>`);
}
if (canRenameAction) {
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Rename' style='background-color:rgba(22,118,123,0.08);color:#16767b;' onclick='showRenameModal("${file.name}")'><i class='fas fa-pen'></i></button>`);
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Rename' style='background-color:var(--primary-opacity-8);color:var(--primary-color);' onclick='showRenameModal("${file.name}")'><i class='fas fa-pen'></i></button>`);
}
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Details' style='background-color:rgba(22,118,123,0.08);color:#16767b;' onclick='showDetailsModal(${idx})'><i class='fas fa-info-circle'></i></button>`);
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Details' style='background-color:var(--primary-opacity-8);color:var(--primary-color);' onclick='showDetailsModal(${idx})'><i class='fas fa-info-circle'></i></button>`);
if (canMove === 'true') {
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Move' style='background-color:rgba(22,118,123,0.08);color:#16767b;' onclick='showMoveModal("${file.name}", "${file.path}")'><i class='fas fa-arrows-alt'></i></button>`);
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Move' style='background-color:var(--primary-opacity-8);color:var(--primary-color);' onclick='showMoveModal("${file.name}", "${file.path}")'><i class='fas fa-arrows-alt'></i></button>`);
}
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='${file.starred ? 'Unstar' : 'Star'}' style='background-color:${file.starred ? 'rgba(255,215,0,0.15)' : 'rgba(22,118,123,0.08)'};color:${file.starred ? '#ffd700' : '#16767b'};' onclick='event.stopPropagation(); toggleStar("${file.name}", "${file.path || ''}")'><i class='fas fa-star'></i></button>`);
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='${file.starred ? 'Unstar' : 'Star'}' style='background-color:${file.starred ? 'var(--warning-opacity-15)' : 'var(--primary-opacity-8)'};color:${file.starred ? 'var(--warning-color)' : 'var(--primary-color)'};' onclick='event.stopPropagation(); toggleStar("${file.name}", "${file.path || ''}")'><i class='fas fa-star'></i></button>`);
if (canDelete === true || canDelete === 'true') {
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Delete' style='background-color:rgba(239,68,68,0.1);color:#b91c1c;' onclick='showDeleteModal("${file.name}", "${file.path}")'><i class='fas fa-trash'></i></button>`);
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Delete' style='background-color:var(--danger-opacity-15);color:var(--danger-color);' onclick='showDeleteModal("${file.name}", "${file.path}")'><i class='fas fa-trash'></i></button>`);
}
} else {
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Open Folder' style='background-color:rgba(22,118,123,0.08);color:#16767b;' onclick='navigateTo(\"${currentPath ? currentPath + '/' : ''}${file.name}\")'><i class='fas fa-folder-open'></i></button>`);
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Open Folder' style='background-color:var(--primary-opacity-8);color:var(--primary-color);' onclick='navigateTo(\"${currentPath ? currentPath + '/' : ''}${file.name}\")'><i class='fas fa-folder-open'></i></button>`);
if (canRenameAction) {
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Rename' style='background-color:rgba(22,118,123,0.08);color:#16767b;' onclick='showRenameModal(\"${file.name}\")'><i class='fas fa-pen'></i></button>`);
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Rename' style='background-color:var(--primary-opacity-8);color:var(--primary-color);' onclick='showRenameModal(\"${file.name}\")'><i class='fas fa-pen'></i></button>`);
}
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Details' style='background-color:rgba(22,118,123,0.08);color:#16767b;' onclick='showDetailsModal(${idx})'><i class='fas fa-info-circle'></i></button>`);
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='${file.starred ? 'Unstar' : 'Star'}' style='background-color:${file.starred ? 'rgba(255,215,0,0.15)' : 'rgba(22,118,123,0.08)'};color:${file.starred ? '#ffd700' : '#16767b'};' onclick='event.stopPropagation(); toggleStar("${file.name}", "${file.path || ''}")'><i class='fas fa-star'></i></button>`);
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Details' style='background-color:var(--primary-opacity-8);color:var(--primary-color);' onclick='showDetailsModal(${idx})'><i class='fas fa-info-circle'></i></button>`);
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='${file.starred ? 'Unstar' : 'Star'}' style='background-color:${file.starred ? 'var(--warning-opacity-15)' : 'var(--primary-opacity-8)'};color:${file.starred ? 'var(--warning-color)' : 'var(--primary-color)'};' onclick='event.stopPropagation(); toggleStar("${file.name}", "${file.path || ''}")'><i class='fas fa-star'></i></button>`);
if (canDelete === true || canDelete === 'true') {
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Delete' style='background-color:rgba(239,68,68,0.1);color:#b91c1c;' onclick='showDeleteModal("${file.name}", "${file.path}")'><i class='fas fa-trash'></i></button>`);
actionsArr.push(`<button class='btn btn-sm file-action-btn' title='Delete' style='background-color:var(--danger-opacity-15);color:var(--danger-color);' onclick='showDeleteModal("${file.name}", "${file.path}")'><i class='fas fa-trash'></i></button>`);
}
}
// Move Delete to the end if present

View File

@@ -25,9 +25,9 @@
<form method="GET" class="d-flex align-items-center w-100 justify-content-between" id="roomFilterForm" style="gap: 1rem;">
<input type="text" name="search" placeholder="Search rooms..." value="{{ search }}" class="form-control flex-grow-1" id="roomSearchInput" autocomplete="off" style="min-width: 0;" />
<button type="button" id="clearRoomsFilter" class="px-4 py-2 rounded-lg text-white font-medium transition-colors duration-200 ms-2 flex-shrink-0"
style="background-color: #16767b; border: 1px solid #16767b;"
onmouseover="this.style.backgroundColor='#1a8a90'"
onmouseout="this.style.backgroundColor='#16767b'">
style="background-color: var(--primary-color); border: 1px solid var(--primary-color);"
onmouseover="this.style.backgroundColor='var(--primary-light)'"
onmouseout="this.style.backgroundColor='var(--primary-color)'">
Clear
</button>
</form>

View File

@@ -13,6 +13,316 @@
) }}
<div class="container-fluid">
<!-- Settings content will go here -->
<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>
<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>
<!-- 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>
</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>
</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');
// 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;
}
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>
{% endblock %}

View File

@@ -5,6 +5,7 @@
{% block extra_css %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/trash.css') }}">
<link rel="stylesheet" href="{{ url_for('main.dynamic_colors') }}">
{% endblock %}
{% block content %}