- Add async initialize() method for reliable initialization waiting - Add static create() factory method for easy async creation - Add initialization state tracking with isInitialized flag - Add warning system for methods called before full initialization - Update server.js to use proper async initialization pattern - Maintain backward compatibility with constructor-only usage - Add accessibility improvement for reduced motion preferences in CSS Fixes the race condition issue where consumers relied on arbitrary timeouts instead of properly waiting for async initialization to complete.
622 lines
12 KiB
CSS
622 lines
12 KiB
CSS
/* CSS Variables for theming */
|
|
:root {
|
|
--bg-color: #f4f4f9;
|
|
--text-color: #333;
|
|
--card-bg: white;
|
|
--border-color: #ddd;
|
|
--input-bg: white;
|
|
--input-border: #ddd;
|
|
--button-bg: #007bff;
|
|
--button-hover: #0056b3;
|
|
--header-bg: transparent;
|
|
--footer-border: #ddd;
|
|
--table-header-bg: #f8f9fa;
|
|
--table-hover: #f8f9fa;
|
|
--toggle-bg: #f8f9fa;
|
|
--toggle-border: #dee2e6;
|
|
--toggle-active-bg: #007bff;
|
|
--shadow: rgba(0,0,0,0.1);
|
|
}
|
|
|
|
/* Dark mode variables */
|
|
[data-theme="dark"] {
|
|
--bg-color: #1a1a1a;
|
|
--text-color: #e0e0e0;
|
|
--card-bg: #2d2d2d;
|
|
--border-color: #404040;
|
|
--input-bg: #333;
|
|
--input-border: #555;
|
|
--button-bg: #4a90e2;
|
|
--button-hover: #357abd;
|
|
--header-bg: transparent;
|
|
--footer-border: #404040;
|
|
--table-header-bg: #3a3a3a;
|
|
--table-hover: #3a3a3a;
|
|
--toggle-bg: #404040;
|
|
--toggle-border: #555;
|
|
--toggle-active-bg: #4a90e2;
|
|
--shadow: rgba(0,0,0,0.3);
|
|
}
|
|
|
|
/* Auto system theme detection */
|
|
@media (prefers-color-scheme: dark) {
|
|
:root[data-theme="auto"] {
|
|
--bg-color: #1a1a1a;
|
|
--text-color: #e0e0e0;
|
|
--card-bg: #2d2d2d;
|
|
--border-color: #404040;
|
|
--input-bg: #333;
|
|
--input-border: #555;
|
|
--button-bg: #4a90e2;
|
|
--button-hover: #357abd;
|
|
--header-bg: transparent;
|
|
--footer-border: #404040;
|
|
--table-header-bg: #3a3a3a;
|
|
--table-hover: #3a3a3a;
|
|
--toggle-bg: #404040;
|
|
--toggle-border: #555;
|
|
--toggle-active-bg: #4a90e2;
|
|
--shadow: rgba(0,0,0,0.3);
|
|
}
|
|
}
|
|
|
|
@media (prefers-color-scheme: light) {
|
|
:root[data-theme="auto"] {
|
|
--bg-color: #f4f4f9;
|
|
--text-color: #333;
|
|
--card-bg: white;
|
|
--border-color: #ddd;
|
|
--input-bg: white;
|
|
--input-border: #ddd;
|
|
--button-bg: #007bff;
|
|
--button-hover: #0056b3;
|
|
--header-bg: transparent;
|
|
--footer-border: #ddd;
|
|
--table-header-bg: #f8f9fa;
|
|
--table-hover: #f8f9fa;
|
|
--toggle-bg: #f8f9fa;
|
|
--toggle-border: #dee2e6;
|
|
--toggle-active-bg: #007bff;
|
|
--shadow: rgba(0,0,0,0.1);
|
|
}
|
|
}
|
|
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
line-height: 1.6;
|
|
margin: 0;
|
|
padding: 0;
|
|
background-color: var(--bg-color);
|
|
color: var(--text-color);
|
|
transition: background-color 0.3s ease, color 0.3s ease;
|
|
}
|
|
|
|
.container {
|
|
max-width: 1000px;
|
|
margin: 0 auto;
|
|
padding: 20px;
|
|
}
|
|
|
|
header {
|
|
text-align: center;
|
|
padding-bottom: 20px;
|
|
}
|
|
|
|
.map-section, .form-section {
|
|
margin-bottom: 20px;
|
|
padding: 15px;
|
|
background-color: var(--card-bg);
|
|
border-radius: 8px;
|
|
box-shadow: 0 2px 4px var(--shadow);
|
|
transition: background-color 0.3s ease;
|
|
}
|
|
|
|
.map-section {
|
|
height: auto;
|
|
min-height: 650px;
|
|
padding-bottom: 20px;
|
|
}
|
|
|
|
#map {
|
|
width: 100%;
|
|
height: 600px;
|
|
border-radius: 8px;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.form-group {
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
input[type="text"], textarea {
|
|
width: calc(100% - 20px);
|
|
padding: 8px;
|
|
margin-top: 5px;
|
|
border: 1px solid var(--input-border);
|
|
border-radius: 4px;
|
|
background-color: var(--input-bg);
|
|
color: var(--text-color);
|
|
transition: background-color 0.3s ease, border-color 0.3s ease;
|
|
}
|
|
|
|
.autocomplete-container {
|
|
position: relative;
|
|
width: 100%;
|
|
}
|
|
|
|
.autocomplete-list {
|
|
position: absolute;
|
|
top: 100%;
|
|
left: 0;
|
|
right: 0;
|
|
background: var(--card-bg);
|
|
border: 1px solid var(--border-color);
|
|
border-top: none;
|
|
border-radius: 0 0 4px 4px;
|
|
max-height: 200px;
|
|
overflow-y: auto;
|
|
z-index: 1000;
|
|
display: none;
|
|
box-shadow: 0 4px 6px var(--shadow);
|
|
}
|
|
|
|
.autocomplete-item {
|
|
padding: 10px;
|
|
cursor: pointer;
|
|
border-bottom: 1px solid #eee;
|
|
}
|
|
|
|
.autocomplete-item:hover,
|
|
.autocomplete-item.selected {
|
|
background-color: var(--table-hover);
|
|
}
|
|
|
|
.autocomplete-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.input-help {
|
|
color: #666;
|
|
font-size: 0.85em;
|
|
margin-top: 4px;
|
|
display: block;
|
|
}
|
|
|
|
|
|
button[type="submit"] {
|
|
background-color: var(--button-bg);
|
|
color: white;
|
|
border: none;
|
|
padding: 10px 20px;
|
|
cursor: pointer;
|
|
border-radius: 4px;
|
|
transition: background-color 0.2s ease-in;
|
|
}
|
|
|
|
button[type="submit"]:hover {
|
|
background-color: var(--button-hover);
|
|
}
|
|
|
|
.message {
|
|
margin-top: 10px;
|
|
padding: 10px;
|
|
display: none;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.success {
|
|
background-color: #d4edda;
|
|
color: #155724;
|
|
}
|
|
|
|
.error {
|
|
background-color: #f8d7da;
|
|
color: #721c24;
|
|
}
|
|
|
|
.message.error.profanity-rejection {
|
|
background-color: #f5c6cb;
|
|
color: #721c24;
|
|
border: 2px solid #dc3545;
|
|
font-weight: bold;
|
|
}
|
|
|
|
/* Only animate for users who haven't requested reduced motion */
|
|
@media (prefers-reduced-motion: no-preference) {
|
|
.message.error.profanity-rejection {
|
|
animation: pulse 0.5s ease-in-out;
|
|
}
|
|
}
|
|
|
|
[data-theme="dark"] .message.error.profanity-rejection {
|
|
background-color: #3d1b1c;
|
|
border-color: #dc3545;
|
|
color: #f5c6cb;
|
|
}
|
|
|
|
@keyframes pulse {
|
|
0% { transform: scale(1); }
|
|
50% { transform: scale(1.02); }
|
|
100% { transform: scale(1); }
|
|
}
|
|
|
|
/* Header layout for theme toggle */
|
|
.header-content {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: flex-start;
|
|
gap: 20px;
|
|
}
|
|
|
|
.header-text {
|
|
flex: 1;
|
|
}
|
|
|
|
/* Theme toggle button */
|
|
.theme-toggle {
|
|
background: var(--card-bg);
|
|
border: 2px solid var(--border-color);
|
|
border-radius: 50%;
|
|
width: 50px;
|
|
height: 50px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
box-shadow: 0 2px 4px var(--shadow);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.theme-toggle:hover {
|
|
transform: scale(1.1);
|
|
box-shadow: 0 4px 8px var(--shadow);
|
|
}
|
|
|
|
.theme-icon {
|
|
font-size: 20px;
|
|
transition: transform 0.3s ease;
|
|
}
|
|
|
|
[data-theme="dark"] .theme-icon {
|
|
transform: rotate(180deg);
|
|
}
|
|
|
|
footer {
|
|
text-align: center;
|
|
padding: 30px 20px;
|
|
margin-top: 30px;
|
|
border-top: 1px solid var(--footer-border);
|
|
clear: both;
|
|
transition: border-color 0.3s ease;
|
|
}
|
|
|
|
.disclaimer {
|
|
font-size: 0.8em;
|
|
color: #777;
|
|
}
|
|
|
|
/* Reports header and toggle */
|
|
.reports-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.view-toggle {
|
|
display: flex;
|
|
gap: 10px;
|
|
}
|
|
|
|
.toggle-btn {
|
|
background-color: var(--toggle-bg);
|
|
border: 2px solid var(--toggle-border);
|
|
color: var(--text-color);
|
|
padding: 8px 16px;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.toggle-btn:hover {
|
|
opacity: 0.8;
|
|
}
|
|
|
|
.toggle-btn.active {
|
|
background-color: var(--toggle-active-bg);
|
|
border-color: var(--toggle-active-bg);
|
|
color: white;
|
|
}
|
|
|
|
.toggle-btn.active:hover {
|
|
opacity: 0.9;
|
|
}
|
|
|
|
/* View containers */
|
|
.view-container {
|
|
width: 100%;
|
|
}
|
|
|
|
/* Table view styles */
|
|
.table-container {
|
|
overflow-x: auto;
|
|
border-radius: 8px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
.reports-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
background: var(--card-bg);
|
|
font-size: 14px;
|
|
transition: background-color 0.3s ease;
|
|
}
|
|
|
|
.reports-table th {
|
|
background-color: var(--table-header-bg);
|
|
color: var(--text-color);
|
|
font-weight: 600;
|
|
padding: 12px;
|
|
text-align: left;
|
|
border-bottom: 2px solid var(--border-color);
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 10;
|
|
}
|
|
|
|
.reports-table td {
|
|
padding: 12px;
|
|
border-bottom: 1px solid var(--border-color);
|
|
vertical-align: top;
|
|
}
|
|
|
|
.reports-table tr:hover {
|
|
background-color: var(--table-hover);
|
|
}
|
|
|
|
.reports-table tr:last-child td {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.table-controls {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.table-info {
|
|
color: #666;
|
|
font-size: 14px;
|
|
}
|
|
|
|
/* Table cell specific styles */
|
|
.location-cell {
|
|
font-weight: 500;
|
|
color: #495057;
|
|
max-width: 250px;
|
|
}
|
|
|
|
.details-cell {
|
|
color: #6c757d;
|
|
font-style: italic;
|
|
max-width: 200px;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.time-cell {
|
|
font-size: 12px;
|
|
color: #868e96;
|
|
}
|
|
|
|
.remaining-cell {
|
|
font-weight: 500;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.remaining-cell.urgent {
|
|
color: #dc3545;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.remaining-cell.warning {
|
|
color: #fd7e14;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.remaining-cell.normal {
|
|
color: #28a745;
|
|
}
|
|
|
|
/* Dark mode table cell overrides - ensure text is visible */
|
|
[data-theme="dark"] .location-cell,
|
|
[data-theme="dark"] .details-cell,
|
|
[data-theme="dark"] .time-cell {
|
|
color: var(--text-color) !important;
|
|
}
|
|
|
|
[data-theme="dark"] .remaining-cell {
|
|
color: var(--text-color) !important;
|
|
}
|
|
|
|
/* Auto theme detection for dark mode */
|
|
@media (prefers-color-scheme: dark) {
|
|
:root[data-theme="auto"] .location-cell,
|
|
:root[data-theme="auto"] .details-cell,
|
|
:root[data-theme="auto"] .time-cell {
|
|
color: var(--text-color) !important;
|
|
}
|
|
|
|
:root[data-theme="auto"] .remaining-cell {
|
|
color: var(--text-color) !important;
|
|
}
|
|
}
|
|
|
|
.loading {
|
|
text-align: center;
|
|
color: #6c757d;
|
|
font-style: italic;
|
|
}
|
|
|
|
/* Responsive adjustments */
|
|
@media (max-width: 768px) {
|
|
.container {
|
|
padding: 10px;
|
|
}
|
|
|
|
.header-content {
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 15px;
|
|
}
|
|
|
|
.header-text {
|
|
text-align: center;
|
|
}
|
|
|
|
.theme-toggle {
|
|
align-self: center;
|
|
}
|
|
|
|
header h1 {
|
|
font-size: 1.5em;
|
|
}
|
|
|
|
header p {
|
|
font-size: 0.9em;
|
|
}
|
|
|
|
.map-section, .form-section {
|
|
padding: 10px;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
#map {
|
|
height: 400px;
|
|
}
|
|
|
|
.form-group {
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
input[type="text"], textarea {
|
|
font-size: 16px; /* Prevents zoom on iOS */
|
|
width: calc(100% - 16px);
|
|
padding: 12px 8px;
|
|
}
|
|
|
|
button[type="submit"] {
|
|
width: 100%;
|
|
padding: 15px;
|
|
font-size: 16px;
|
|
}
|
|
|
|
.reports-header {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
gap: 15px;
|
|
text-align: center;
|
|
}
|
|
|
|
.reports-header h2 {
|
|
font-size: 1.3em;
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.view-toggle {
|
|
justify-content: center;
|
|
}
|
|
|
|
.toggle-btn {
|
|
flex: 1;
|
|
font-size: 14px;
|
|
padding: 12px 8px;
|
|
}
|
|
|
|
.reports-table {
|
|
font-size: 12px;
|
|
}
|
|
|
|
.reports-table th,
|
|
.reports-table td {
|
|
padding: 8px 4px;
|
|
}
|
|
|
|
.location-cell {
|
|
max-width: 120px;
|
|
font-size: 11px;
|
|
}
|
|
|
|
.details-cell {
|
|
max-width: 100px;
|
|
font-size: 11px;
|
|
}
|
|
|
|
.time-cell {
|
|
font-size: 10px;
|
|
}
|
|
|
|
.remaining-cell {
|
|
font-size: 10px;
|
|
}
|
|
|
|
.autocomplete-list {
|
|
max-height: 150px;
|
|
}
|
|
|
|
.input-help {
|
|
font-size: 0.8em;
|
|
}
|
|
|
|
footer {
|
|
padding: 20px 10px;
|
|
font-size: 0.9em;
|
|
}
|
|
}
|
|
|
|
/* Extra small screens */
|
|
@media (max-width: 480px) {
|
|
.container {
|
|
padding: 5px;
|
|
}
|
|
|
|
header h1 {
|
|
font-size: 1.3em;
|
|
}
|
|
|
|
.map-section, .form-section {
|
|
padding: 8px;
|
|
}
|
|
|
|
#map {
|
|
height: 300px;
|
|
}
|
|
|
|
.reports-table th {
|
|
font-size: 10px;
|
|
padding: 6px 2px;
|
|
}
|
|
|
|
.reports-table td {
|
|
font-size: 10px;
|
|
padding: 6px 2px;
|
|
}
|
|
|
|
.location-cell {
|
|
max-width: 100px;
|
|
}
|
|
|
|
.details-cell {
|
|
max-width: 80px;
|
|
}
|
|
|
|
.toggle-btn {
|
|
font-size: 12px;
|
|
padding: 10px 6px;
|
|
}
|
|
}
|