Files
TimeTracker/desktop/src/renderer/css/styles.css
T
Dries Peeters 4c427c92c0 feat(desktop): centralize connection lifecycle and expand login flow
Introduce a connection manager (state machine, listeners, offline handling,
token/server binding) with timer start/stop reconciliation helpers. Refactor
app.js to use it and extend the first-run wizard (welcome + server + token).

Add node:test coverage for the manager, timer operations, and an /api/v1/info
integration harness. Rebuild renderer bundle for packaging.

Bump desktop toolchain (electron, electron-builder, esbuild) and Python
package version to 5.3.2 in setup.py.
2026-04-24 16:17:56 +02:00

842 lines
13 KiB
CSS

* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
/* Brand Colors - Unified with web app */
--primary: #4A90E2;
--primary-dark: #3b82f6;
--secondary: #50E3C2;
--secondary-dark: #06b6d4;
/* Status Colors */
--error: #E53935;
--success: #4CAF50;
--warning: #FF9800;
/* Background Colors */
--bg: #ffffff;
--bg-secondary: #F7F9FB;
--bg-dark: #1A202C;
--bg-dark-secondary: #2D3748;
/* Text Colors */
--text: #2D3748;
--text-secondary: #A0AEC0;
--text-dark: #E2E8F0;
--text-dark-secondary: #718096;
/* Border Colors */
--border: #E2E8F0;
--border-dark: #4A5568;
/* Shadow */
--shadow: rgba(0, 0, 0, 0.1);
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: var(--bg);
color: var(--text);
overflow: hidden;
-webkit-app-region: drag;
}
button, input, select, textarea {
-webkit-app-region: no-drag;
}
.screen {
display: none;
width: 100vw;
height: 100vh;
overflow: hidden;
}
.screen.active {
display: flex;
flex-direction: column;
}
/* Loading Screen */
#loading-screen {
align-items: center;
justify-content: center;
background: var(--bg);
}
.loading-container {
text-align: center;
}
.loading-logo {
width: 120px;
height: 120px;
margin: 0 auto 32px;
animation: logoPulse 2s ease-in-out infinite;
}
@keyframes logoPulse {
0%, 100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.8;
transform: scale(0.95);
}
}
.loading-spinner {
width: 48px;
height: 48px;
border: 4px solid var(--border);
border-top-color: var(--primary);
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 24px;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Login Screen */
#login-screen {
align-items: center;
justify-content: center;
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
}
.login-container {
background: var(--bg);
padding: 48px;
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
width: 100%;
max-width: 400px;
}
.login-header {
text-align: center;
margin-bottom: 32px;
}
.logo-image {
width: 80px;
height: 80px;
margin: 0 auto 16px;
display: block;
}
.icon-large {
font-size: 64px;
margin-bottom: 16px;
}
.login-header h1 {
font-size: 28px;
margin-bottom: 8px;
color: var(--text);
}
.login-header p {
color: var(--text-secondary);
}
.wizard-step-label {
font-size: 13px;
font-weight: 600;
color: var(--text-secondary);
margin-bottom: 16px;
}
.wizard-actions {
display: flex;
flex-wrap: wrap;
gap: 10px;
align-items: center;
}
.wizard-actions .btn {
flex: 1;
min-width: 120px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: var(--text);
}
.form-group input {
width: 100%;
padding: 12px;
border: 1px solid var(--border);
border-radius: 6px;
font-size: 14px;
}
.form-group small {
display: block;
margin-top: 4px;
color: var(--text-secondary);
font-size: 12px;
}
.error-message {
margin-top: 16px;
padding: 12px;
background: #ffebee;
color: var(--error);
border-radius: 6px;
display: none;
}
.error-message.show {
display: block;
}
/* Main App */
#main-screen {
display: flex;
flex-direction: column;
}
.app-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
background: var(--bg-secondary);
border-bottom: 1px solid var(--border);
-webkit-app-region: drag;
}
.header-left {
display: flex;
align-items: center;
gap: 12px;
flex: 1;
min-width: 0;
}
.header-connection {
display: flex;
align-items: center;
gap: 10px;
min-width: 0;
margin-left: 4px;
}
.header-connection-details {
display: flex;
flex-direction: column;
gap: 2px;
min-width: 0;
font-size: 11px;
line-height: 1.25;
color: var(--text-secondary);
}
.header-connection-url {
font-weight: 500;
color: var(--text-primary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 36vw;
}
.header-connection-meta {
font-size: 10px;
color: var(--text-secondary);
}
.wizard-intro {
margin: 0 0 16px;
line-height: 1.45;
color: var(--text-secondary);
font-size: 14px;
}
.header-left h1 {
font-size: 18px;
font-weight: 600;
margin: 0;
}
.connection-status {
font-size: 12px;
width: 12px;
height: 12px;
border-radius: 50%;
display: inline-block;
}
.connection-connected {
color: var(--success);
}
.connection-error {
color: var(--error);
}
.connection-disconnected {
color: var(--text-secondary);
}
.connection-offline {
color: var(--error);
}
.connection-connecting {
color: var(--warning, #b8860b);
}
.header-right {
display: flex;
gap: 8px;
-webkit-app-region: no-drag;
}
.header-btn {
width: 32px;
height: 32px;
border: none;
background: transparent;
cursor: pointer;
border-radius: 4px;
font-size: 18px;
display: flex;
align-items: center;
justify-content: center;
}
.header-btn:hover {
background: rgba(0, 0, 0, 0.1);
}
.app-nav {
display: flex;
gap: 4px;
padding: 8px;
background: var(--bg-secondary);
border-bottom: 1px solid var(--border);
}
.nav-btn {
padding: 8px 16px;
border: none;
background: transparent;
cursor: pointer;
border-radius: 4px;
font-size: 14px;
color: var(--text-secondary);
}
.nav-btn:hover {
background: rgba(0, 0, 0, 0.05);
}
.nav-btn.active {
background: var(--primary);
color: white;
}
.app-content {
flex: 1;
overflow-y: auto;
padding: 24px;
}
.view {
display: none;
}
.view.active {
display: block;
}
/* Dashboard */
.dashboard-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 24px;
}
.dashboard-card {
background: var(--bg);
border: 1px solid var(--border);
border-radius: 8px;
padding: 24px;
box-shadow: 0 2px 4px var(--shadow);
}
.dashboard-card.full-width {
grid-column: 1 / -1;
}
.dashboard-card h2 {
font-size: 16px;
font-weight: 600;
margin-bottom: 16px;
color: var(--text-secondary);
}
.timer-display {
text-align: center;
margin: 24px 0;
}
.timer-time {
font-size: 48px;
font-weight: bold;
font-variant-numeric: tabular-nums;
color: var(--primary);
}
.timer-project {
margin-top: 8px;
color: var(--text-secondary);
}
.timer-controls {
display: flex;
gap: 8px;
}
.summary-time {
font-size: 32px;
font-weight: bold;
color: var(--primary);
}
/* Buttons */
.btn {
padding: 10px 20px;
border: none;
border-radius: 6px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: background 0.2s;
}
.btn-primary {
background: var(--primary);
color: white;
}
.btn-primary:hover {
background: var(--primary-dark);
}
.btn-secondary {
background: var(--bg-secondary);
color: var(--text);
}
.btn-secondary:hover {
background: var(--border);
}
.btn-danger {
background: var(--error);
color: white;
}
.btn-danger:hover {
background: #c62828;
}
/* Utilities */
.empty-state {
text-align: center;
padding: 48px;
color: var(--text-secondary);
}
.view-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.view-header h2 {
font-size: 24px;
font-weight: 600;
}
/* Projects Grid */
.projects-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 16px;
}
.project-card {
background: var(--bg);
border: 1px solid var(--border);
border-radius: 8px;
padding: 16px;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
}
.project-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px var(--shadow);
}
/* Entries List */
.entries-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.entry-item {
background: var(--bg);
border: 1px solid var(--border);
border-radius: 6px;
padding: 16px;
display: flex;
justify-content: space-between;
align-items: center;
}
.entry-info h3 {
font-size: 16px;
margin-bottom: 4px;
}
.entry-info p {
color: var(--text-secondary);
font-size: 14px;
}
.entry-time {
font-size: 18px;
font-weight: 600;
color: var(--primary);
}
/* Settings */
.settings-section {
margin-bottom: 32px;
padding-bottom: 32px;
border-bottom: 1px solid var(--border);
}
.settings-section:last-child {
border-bottom: none;
}
.settings-section h3 {
font-size: 18px;
font-weight: 600;
margin-bottom: 16px;
}
.settings-section .form-group {
margin-bottom: 20px;
}
.settings-section .form-group label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: var(--text);
}
.settings-section .form-group input {
width: 100%;
padding: 10px;
border: 1px solid var(--border);
border-radius: 4px;
font-size: 14px;
background: var(--bg);
color: var(--text);
}
.settings-section .form-group input:focus {
outline: none;
border-color: var(--primary);
}
.settings-section .form-group input:disabled {
background: var(--bg-secondary);
color: var(--text-secondary);
cursor: not-allowed;
}
.settings-section .form-group small {
display: block;
margin-top: 4px;
font-size: 12px;
color: var(--text-secondary);
}
.settings-section .form-group button {
margin-right: 10px;
margin-top: 10px;
}
#settings-message {
display: none;
padding: 12px;
border-radius: 4px;
margin-top: 16px;
font-size: 14px;
}
#settings-message.message-success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
#settings-message.message-error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
#settings-message.message-warning {
background-color: #fff3cd;
color: #856404;
border: 1px solid #ffeaa7;
}
#settings-message.message-info {
background-color: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
/* Modal */
.modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-content {
background: var(--bg);
border-radius: 8px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
max-width: 90%;
max-height: 90%;
overflow: auto;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 24px;
border-bottom: 1px solid var(--border);
}
.modal-header h3 {
margin: 0;
font-size: 20px;
font-weight: 600;
}
.modal-close {
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: var(--text-secondary);
padding: 0;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
}
.modal-close:hover {
background: var(--bg-secondary);
}
.modal-body {
padding: 24px;
}
.modal-footer {
display: flex;
justify-content: flex-end;
gap: 8px;
padding: 16px 24px;
border-top: 1px solid var(--border);
}
.form-control {
width: 100%;
padding: 10px;
border: 1px solid var(--border);
border-radius: 6px;
font-size: 14px;
background: var(--bg);
color: var(--text);
}
.form-control:focus {
outline: none;
border-color: var(--primary);
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
/* Filters */
.entries-filters {
background: var(--bg-secondary);
padding: 16px;
border-radius: 8px;
margin-bottom: 16px;
}
.filter-row {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.filter-row label {
font-size: 14px;
font-weight: 500;
color: var(--text);
}
.filter-row input,
.filter-row select {
padding: 8px;
border: 1px solid var(--border);
border-radius: 4px;
font-size: 14px;
}
.view-header-actions {
display: flex;
gap: 8px;
}
.btn-sm {
padding: 6px 12px;
font-size: 12px;
}
/* Entry item enhancements */
.entry-item {
display: flex;
justify-content: space-between;
align-items: flex-start;
padding: 16px;
}
.entry-info {
flex: 1;
}
.entry-task {
color: var(--text-secondary);
font-size: 13px;
margin-top: 4px;
}
.entry-time-range {
color: var(--text-secondary);
font-size: 13px;
margin-top: 4px;
}
.entry-notes {
color: var(--text-secondary);
font-size: 13px;
margin-top: 8px;
font-style: italic;
}
.entry-tags {
color: var(--text-secondary);
font-size: 12px;
margin-top: 4px;
}
.entry-actions {
display: flex;
align-items: center;
gap: 12px;
}
.badge {
display: inline-block;
padding: 4px 8px;
border-radius: 12px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
}
.badge-success {
background: #d4edda;
color: #155724;
}
.timer-task,
.timer-notes {
margin-top: 8px;
color: var(--text-secondary);
font-size: 14px;
}
/* Notifications */
.notification {
position: fixed;
top: 20px;
right: 20px;
padding: 16px 20px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
z-index: 2000;
max-width: 400px;
display: none;
}
.notification-error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.notification-success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}