solventum-image-metadata/static/css/app.css
SamoilenkoVadym 3deaa5ef40 Initial commit: Oliver Metadata Tool (FastAPI)
Complete Flask → FastAPI migration with:
- FastAPI app with session auth, Azure AD SSO, rate limiting
- SQLite-backed session store (survives restarts)
- Bulk AI metadata generation with SSE progress
- Admin panel (user management, audit log, AI usage)
- Subpath deployment support (ROOT_PATH config)
- Docker + deploy.sh for production deployment
- Test suite (auth, upload, templates, imports, admin, sessions)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-09 21:23:42 +00:00

811 lines
21 KiB
CSS

/* ========== CSS VARIABLES ========== */
:root {
/* Main colors */
--primary-gold: #FFC407;
--primary-gold-dark: #e6b007;
--primary-gold-light: #ffcf33;
/* Dark colors */
--dark-primary: #2c2c2c;
--dark-secondary: #1a1a1a;
/* Light colors */
--white: #ffffff;
--light-bg: #fafafa;
--light-bg-gradient: #f8fafc;
/* Text colors */
--text-primary: #1f2937;
--text-secondary: #374151;
--text-muted: #6b7280;
/* Status colors */
--success-green: #4ade80;
--error-red: #ef4444;
/* Opacity */
--overlay-light: rgba(255, 255, 255, 0.95);
--overlay-dark: rgba(0, 0, 0, 0.5);
--border-light: rgba(255, 255, 255, 0.2);
--border-subtle: rgba(0, 0, 0, 0.05);
/* Shadows */
--shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.1);
--shadow-md: 0 10px 25px rgba(0, 0, 0, 0.15);
--shadow-lg: 0 20px 40px rgba(0, 0, 0, 0.1);
/* Radius */
--radius-sm: 4px;
--radius-md: 12px;
--radius-lg: 18px;
--radius-xl: 20px;
/* Spacing */
--spacing-xs: 4px;
--spacing-sm: 8px;
--spacing-md: 12px;
--spacing-lg: 16px;
--spacing-xl: 20px;
--spacing-2xl: 25px;
/* Fonts */
--font-family: 'Montserrat', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
/* Transitions */
--transition-fast: 0.15s ease;
--transition-normal: 0.3s ease;
--transition-slow: 0.5s ease;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: var(--font-family);
background: linear-gradient(135deg, var(--dark-primary) 0%, var(--dark-secondary) 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: var(--overlay-light);
backdrop-filter: blur(20px);
border-radius: var(--radius-xl);
box-shadow: var(--shadow-lg);
overflow: hidden;
border: 1px solid var(--border-light);
}
.header {
background: linear-gradient(135deg, var(--primary-gold) 0%, var(--primary-gold-dark) 100%);
color: var(--dark-secondary);
padding: 30px;
text-align: center;
position: relative;
}
.header::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(45deg, transparent 30%, rgba(255,255,255,0.1) 50%, transparent 70%);
animation: shimmer 3s infinite;
pointer-events: none;
}
.header h1 {
font-size: 28px;
margin-bottom: 10px;
font-weight: 600;
position: relative;
z-index: 1;
}
.header p {
opacity: 0.9;
font-size: 14px;
position: relative;
z-index: 1;
}
@keyframes shimmer {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
.content {
padding: 40px;
background: linear-gradient(180deg, var(--light-bg) 0%, var(--light-bg-gradient) 100%);
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
.upload-section {
background: var(--white);
border-radius: var(--radius-md);
padding: 20px;
margin-bottom: 30px;
box-shadow: var(--shadow-sm);
}
.upload-area {
border: 3px dashed var(--primary-gold);
border-radius: var(--radius-md);
padding: 60px 20px;
text-align: center;
cursor: pointer;
transition: all var(--transition-normal);
background: var(--light-bg);
margin-bottom: 20px;
}
.upload-area:hover {
background: #fffbf0;
border-color: var(--primary-gold-dark);
transform: translateY(-2px);
}
.upload-area.dragover {
background: #fff9e6;
transform: scale(1.02);
border-color: var(--primary-gold-dark);
}
#fileInput { display: none; }
.upload-icon { font-size: 48px; margin-bottom: 15px; }
.output-dir-section {
display: flex;
align-items: center;
gap: 15px;
margin-bottom: 20px;
padding: 15px;
background: white;
border-radius: 8px;
}
.output-dir-section label {
font-weight: 600;
color: #495057;
min-width: 120px;
}
#outputDir {
flex: 1;
padding: 10px;
border: 2px solid #dee2e6;
border-radius: var(--radius-sm);
font-size: 14px;
font-family: var(--font-family);
transition: border-color var(--transition-fast);
}
#outputDir:focus {
outline: none;
border-color: var(--primary-gold);
}
.output-dir-hint {
font-size: 12px;
color: #6c757d;
margin-top: 5px;
}
.btn {
background: linear-gradient(135deg, var(--primary-gold), var(--primary-gold-dark));
color: var(--dark-secondary);
border: none;
padding: 12px 30px;
border-radius: var(--radius-md);
cursor: pointer;
font-size: 16px;
font-weight: 600;
font-family: var(--font-family);
transition: all var(--transition-fast);
margin: 5px;
}
.btn:hover:not(:disabled) {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(255, 196, 7, 0.4);
}
.btn:active:not(:disabled) {
transform: translateY(0);
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
.btn-small {
padding: 8px 20px;
font-size: 14px;
}
.progress-bar {
width: 100%;
height: 30px;
background: #e9ecef;
border-radius: 15px;
overflow: hidden;
margin: 20px 0;
display: none;
}
.progress-fill {
height: 100%;
background: linear-gradient(135deg, var(--primary-gold), var(--primary-gold-dark));
transition: width var(--transition-normal);
display: flex;
align-items: center;
justify-content: center;
color: var(--dark-secondary);
font-weight: 600;
font-size: 14px;
}
.file-list {
margin-top: 30px;
display: none;
}
.batch-toolbar {
background: var(--white);
border-radius: var(--radius-md);
padding: 15px;
margin-bottom: 20px;
display: flex;
justify-content: space-between;
align-items: center;
gap: 15px;
box-shadow: var(--shadow-sm);
}
.batch-toolbar-left {
display: flex;
gap: 10px;
align-items: center;
}
.batch-toolbar-right {
display: flex;
gap: 10px;
}
.btn-toolbar {
background: #6c757d;
color: white;
border: none;
padding: 8px 16px;
border-radius: 20px;
cursor: pointer;
font-size: 13px;
font-weight: 600;
transition: transform 0.2s;
}
.btn-toolbar:hover {
transform: translateY(-2px);
background: #5a6268;
}
.btn-export {
background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
}
.btn-export:hover {
background: linear-gradient(135deg, #218838 0%, #1fa589 100%);
}
.selection-count {
font-size: 13px;
color: #495057;
font-weight: 600;
}
.file-item {
background: var(--white);
border-radius: var(--radius-md);
padding: 20px;
margin-bottom: 20px;
border-left: 4px solid var(--primary-gold);
box-shadow: var(--shadow-sm);
transition: all var(--transition-fast);
}
.file-item:hover {
box-shadow: var(--shadow-md);
transform: translateX(2px);
}
.file-item.selected {
background: #fffbf0;
border-left-color: var(--success-green);
}
.file-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.file-header-left {
display: flex;
align-items: center;
gap: 12px;
}
.file-checkbox {
width: 20px;
height: 20px;
cursor: pointer;
}
.file-name {
font-weight: 600;
font-size: 16px;
color: #495057;
}
.file-type {
background: linear-gradient(135deg, var(--primary-gold), var(--primary-gold-dark));
color: var(--dark-secondary);
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
}
.metadata-comparison {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
}
.metadata-box {
background: var(--light-bg);
border-radius: var(--radius-sm);
padding: 15px;
border: 1px solid var(--border-subtle);
}
.metadata-box h4 {
color: var(--primary-gold-dark);
margin-bottom: 10px;
font-size: 14px;
font-weight: 600;
}
.metadata-item {
display: flex;
flex-direction: column;
padding: 8px 0;
border-bottom: 1px solid #dee2e6;
}
.metadata-item:last-child { border-bottom: none; }
.metadata-label { font-weight: 600; color: #495057; font-size: 12px; margin-bottom: 4px; }
.metadata-value { color: #6c757d; font-size: 13px; }
.alert {
padding: 15px;
border-radius: 8px;
margin: 15px 0;
display: none;
}
.alert-error { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
.alert-success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
.alert-info { background: #d1ecf1; color: #0c5460; border: 1px solid #bee5eb; }
.actions {
text-align: center;
margin-top: 20px;
}
.spinner {
border: 3px solid #f3f3f3;
border-top: 3px solid var(--primary-gold);
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 20px auto;
display: none;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.footer {
text-align: center;
padding: 20px;
color: #6c757d;
font-size: 12px;
border-top: 1px solid #dee2e6;
}
/* Metadata Source Selector */
.metadata-source-selector {
background: white;
border-radius: 8px;
padding: 15px;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 15px;
}
.metadata-source-selector label {
font-weight: 600;
color: #495057;
min-width: 140px;
}
.source-select {
flex: 1;
padding: 10px;
border: 2px solid var(--primary-gold);
border-radius: var(--radius-sm);
font-size: 14px;
font-family: var(--font-family);
cursor: pointer;
background: var(--white);
transition: border-color var(--transition-fast);
}
.source-select:focus {
outline: none;
border-color: var(--primary-gold-dark);
}
.source-info {
font-size: 12px;
color: #6c757d;
margin-left: 10px;
}
/* Editable Metadata Fields */
.editable-field {
width: 100%;
padding: 8px;
border: 2px solid #dee2e6;
border-radius: 5px;
font-size: 13px;
font-family: inherit;
transition: border-color 0.3s;
}
.editable-field:focus {
outline: none;
border-color: var(--primary-gold);
box-shadow: 0 0 0 3px rgba(255, 196, 7, 0.1);
}
.editable-field.invalid {
border-color: #dc3545;
}
textarea.editable-field {
min-height: 60px;
resize: vertical;
}
.char-count {
font-size: 11px;
color: #6c757d;
margin-top: 4px;
display: block;
}
.char-count.warning {
color: #ffc107;
}
.char-count.danger {
color: #dc3545;
}
.metadata-field {
margin-bottom: 15px;
}
.metadata-field label {
display: block;
font-weight: 600;
color: #495057;
font-size: 12px;
margin-bottom: 5px;
}
/* File Action Buttons */
.file-actions {
display: flex;
gap: 10px;
margin-top: 15px;
}
.btn-save {
background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
color: white;
border: none;
padding: 8px 20px;
border-radius: 20px;
cursor: pointer;
font-size: 14px;
font-weight: 600;
transition: transform 0.2s;
}
.btn-save:hover {
transform: translateY(-2px);
}
.btn-save:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
.btn-reset {
background: #6c757d;
color: white;
border: none;
padding: 8px 20px;
border-radius: 20px;
cursor: pointer;
font-size: 14px;
font-weight: 600;
transition: transform 0.2s;
}
.btn-reset:hover {
transform: translateY(-2px);
background: #5a6268;
}
/* Import Metadata Section */
.import-section {
background: white;
border-radius: 8px;
padding: 15px;
margin-bottom: 15px;
border: 2px dashed #dee2e6;
}
.import-section.active {
border-color: var(--success-green);
background: #f0fff4;
}
.btn-import {
background: linear-gradient(135deg, #17a2b8 0%, #138496 100%);
color: white;
border: none;
padding: 8px 20px;
border-radius: 20px;
cursor: pointer;
font-size: 14px;
font-weight: 600;
transition: transform 0.2s;
}
.btn-import:hover {
transform: translateY(-2px);
}
.import-stats {
font-size: 12px;
color: #28a745;
margin-top: 10px;
padding: 8px;
background: white;
border-radius: 5px;
}
/* Template Section */
.template-section {
background: white;
border-radius: 8px;
padding: 15px;
margin-bottom: 15px;
border: 2px dashed #dee2e6;
}
.template-section.active {
border-color: var(--primary-gold);
background: #fffbf0;
}
.template-controls {
display: flex;
gap: 10px;
align-items: center;
flex-wrap: wrap;
}
.template-select {
flex: 1;
min-width: 200px;
padding: 8px;
border: 2px solid var(--primary-gold);
border-radius: var(--radius-sm);
font-size: 13px;
font-family: var(--font-family);
cursor: pointer;
transition: border-color var(--transition-fast);
}
.template-select:focus {
outline: none;
border-color: var(--primary-gold-dark);
}
.btn-template {
background: linear-gradient(135deg, var(--primary-gold), var(--primary-gold-dark));
color: var(--dark-secondary);
border: none;
padding: 8px 16px;
border-radius: var(--radius-md);
cursor: pointer;
font-size: 13px;
font-weight: 600;
font-family: var(--font-family);
transition: all var(--transition-fast);
}
.btn-template:hover:not(:disabled) {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(255, 196, 7, 0.3);
}
.btn-template:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
.template-preview {
margin-top: 10px;
padding: 10px;
background: white;
border-radius: 5px;
font-size: 12px;
color: #495057;
display: none;
}
.template-preview-item {
margin-bottom: 5px;
}
.template-preview-label {
font-weight: 600;
color: var(--primary-gold-dark);
}
/* Modal Styles */
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.5);
}
.modal-content {
background-color: white;
margin: 5% auto;
padding: 30px;
border-radius: 15px;
width: 90%;
max-width: 600px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.modal-header h3 {
color: var(--primary-gold-dark);
margin: 0;
font-weight: 600;
}
.close-modal {
font-size: 28px;
font-weight: bold;
color: #aaa;
cursor: pointer;
}
.close-modal:hover {
color: #000;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
font-weight: 600;
color: #495057;
margin-bottom: 5px;
font-size: 13px;
}
.form-group input,
.form-group textarea {
width: 100%;
padding: 10px;
border: 2px solid #dee2e6;
border-radius: var(--radius-sm);
font-size: 13px;
font-family: var(--font-family);
transition: border-color var(--transition-fast);
}
.form-group input:focus,
.form-group textarea:focus {
outline: none;
border-color: var(--primary-gold);
box-shadow: 0 0 0 3px rgba(255, 196, 7, 0.1);
}
.form-group textarea {
min-height: 60px;
resize: vertical;
}
.form-group small {
font-size: 11px;
color: #6c757d;
margin-top: 3px;
display: block;
}
.variable-hint {
background: #fffbf0;
padding: 8px;
border-radius: var(--radius-sm);
font-size: 11px;
color: var(--primary-gold-dark);
margin-top: 5px;
border: 1px solid rgba(255, 196, 7, 0.2);
}
@media (max-width: 768px) {
.metadata-comparison {
grid-template-columns: 1fr;
}
.metadata-source-selector {
flex-direction: column;
align-items: flex-start;
}
.metadata-source-selector label {
min-width: auto;
}
}