Refactor: Complete codebase reorganization and modernization

- Split server.js routes into modular files (server/routes/)
  - departures.js: Departure data endpoints
  - sites.js: Site search and nearby sites
  - config.js: Configuration endpoints

- Reorganized file structure following Node.js best practices:
  - Moved sites-config.json to config/sites.json
  - Moved API_RESPONSE_DOCUMENTATION.md to docs/
  - Moved raspberry-pi-setup.sh to scripts/
  - Archived legacy files to archive/ directory

- Updated all code references to new file locations
- Added archive/ to .gitignore to exclude legacy files from repo
- Updated README.md with new structure and organization
- All functionality tested and working correctly

Version: 1.2.0
This commit is contained in:
2026-01-01 10:51:58 +01:00
parent d15142f1c6
commit 392a50b535
28 changed files with 3197 additions and 3295 deletions

349
public/css/main.css Normal file
View File

@@ -0,0 +1,349 @@
/* Base styles */
body {
font-family: Arial, sans-serif;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
color: #333;
transition: all 0.5s ease;
}
/* Auto-apply landscape layout for wide screens */
@media (min-width: 1200px) {
body:not(.vertical):not(.upsidedown):not(.vertical-reverse) {
max-width: 100%;
padding: 8px 12px 0 12px; /* Minimal padding to maximize space */
padding-bottom: 0;
}
body:not(.vertical):not(.upsidedown):not(.vertical-reverse) #content-wrapper {
display: grid;
grid-template-rows: auto 1fr auto;
gap: 8px; /* Reduced gap */
height: 100vh;
max-height: 100vh;
overflow: hidden;
}
body:not(.vertical):not(.upsidedown):not(.vertical-reverse) .clock-container {
grid-row: 1;
margin-bottom: 0;
padding: 6px 16px; /* Reduced padding */
}
body:not(.vertical):not(.upsidedown):not(.vertical-reverse) .main-content-grid {
grid-row: 2;
display: block;
overflow-y: auto;
overflow-x: hidden;
min-height: 0;
width: 100%;
}
body:not(.vertical):not(.upsidedown):not(.vertical-reverse) .departure-container {
display: grid;
grid-template-columns: repeat(4, 1fr); /* Fixed 4 columns to use all space */
gap: 6px; /* Minimal gap */
margin-bottom: 0;
width: 100%;
box-sizing: border-box;
padding: 0; /* Remove any padding */
}
/* Ensure each column uses equal space */
body:not(.vertical):not(.upsidedown):not(.vertical-reverse) .departure-container > * {
min-width: 0; /* Allow flex shrinking */
max-width: 100%; /* Prevent overflow */
}
/* Weather fixed at bottom */
body:not(.vertical):not(.upsidedown):not(.vertical-reverse) .weather-section {
grid-row: 3;
position: sticky;
bottom: 0;
background-color: inherit;
padding: 8px 0; /* Reduced padding */
margin-top: 0;
}
body:not(.vertical):not(.upsidedown):not(.vertical-reverse) .weather-container {
margin: 0;
max-width: 100%;
}
}
/* Dark mode styles */
body.dark-mode {
background-color: #222;
color: #f5f5f5;
}
body.dark-mode .departure-card {
background-color: #333;
border-left-color: #0077cc;
}
body.dark-mode .config-modal-content {
background-color: #333;
color: #f5f5f5;
}
body.dark-mode .config-modal-body {
background-color: #333;
}
body.dark-mode .config-modal-footer {
background-color: #444;
}
body.dark-mode #config-cancel-button {
background-color: #555;
color: #f5f5f5;
}
body.dark-mode .time,
body.dark-mode .destination {
color: #f5f5f5;
}
body.dark-mode .direction,
body.dark-mode .details,
body.dark-mode .countdown,
body.dark-mode .last-updated {
color: #aaa;
}
body.dark-mode h2 {
color: #0077cc;
}
body.dark-mode .sun-times {
color: #aaa;
}
body.dark-mode .line-number {
background-color: #0077cc;
}
/* Normal orientation */
body.normal {
max-width: 800px;
}
body.normal .departure-container {
display: grid;
grid-template-columns: 1fr;
gap: 10px;
}
/* Landscape orientation - Optimized for wide screens */
body.landscape {
max-width: 100%;
padding: 20px 40px;
}
/* Main content area: clock at top, then two-column layout below */
body.landscape #content-wrapper {
display: grid;
grid-template-rows: auto 1fr;
gap: 20px;
height: 100vh;
max-height: 100vh;
overflow: hidden;
}
body.landscape .clock-container {
grid-row: 1;
margin-bottom: 0;
}
/* Main content grid: departures on left, weather on right */
body.landscape .main-content-grid {
grid-row: 2;
display: grid;
grid-template-columns: 1fr 380px;
gap: 20px;
overflow: hidden;
min-height: 0;
}
/* Departures container: multi-column grid */
body.landscape .departure-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(450px, 1fr));
gap: 15px;
overflow-y: auto;
overflow-x: hidden;
padding-right: 10px;
min-height: 0;
}
/* Weather container: fixed width, scrollable */
body.landscape .weather-container {
overflow-y: auto;
overflow-x: hidden;
max-height: 100%;
position: sticky;
top: 0;
align-self: start;
}
/* Better horizontal space usage in landscape */
body.landscape .departure-card {
min-height: 120px;
}
body.landscape .line-number-box {
min-width: 120px;
width: 120px;
}
body.landscape .line-number-large {
font-size: 3.5em;
}
/* Site containers in landscape should be more compact */
body.landscape .site-container {
margin-bottom: 15px;
}
body.landscape .site-header {
font-size: 1em;
padding: 8px 12px;
}
/* Vertical orientation (90 degrees rotated) */
body.vertical {
max-width: 100%;
height: 100vh;
padding: 0;
margin: 0;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
body.vertical #content-wrapper {
transform: rotate(90deg);
transform-origin: center center;
position: absolute;
width: 100vh; /* Use viewport height for width */
height: 100vw; /* Use viewport width for height */
max-width: 800px; /* Limit width for better readability */
padding: 20px;
box-sizing: border-box;
overflow-y: auto;
background-color: transparent; /* Remove background color */
left: 50%;
top: 50%;
margin-left: -50vh; /* Half of width */
margin-top: -50vw; /* Half of height */
}
body.vertical .config-button {
transform: rotate(-90deg);
position: fixed;
right: 10px;
bottom: 10px; /* Changed from top to bottom */
z-index: 1000;
}
body.vertical .departure-container {
display: grid;
grid-template-columns: 1fr;
gap: 10px;
}
/* Upside down orientation (180 degrees rotated) */
body.upsidedown {
max-width: 100%;
height: 100vh;
padding: 0;
margin: 0;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
body.upsidedown #content-wrapper {
transform: rotate(180deg);
transform-origin: center center;
position: absolute;
width: 100%;
max-width: 800px;
padding: 20px;
box-sizing: border-box;
overflow-y: auto;
background-color: transparent; /* Remove background color */
left: 50%;
top: 50%;
margin-left: -400px; /* Half of max-width */
margin-top: -50vh; /* Half of viewport height */
}
body.upsidedown .config-button {
transform: rotate(-180deg);
position: fixed;
right: 10px;
bottom: 10px; /* Changed from top to bottom */
z-index: 1000;
}
body.upsidedown .departure-container {
display: grid;
grid-template-columns: 1fr;
gap: 10px;
}
/* Vertical reverse orientation (270 degrees rotated) */
body.vertical-reverse {
max-width: 100%;
height: 100vh;
padding: 0;
margin: 0;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
body.vertical-reverse #content-wrapper {
transform: rotate(270deg);
transform-origin: center center;
position: absolute;
width: 100vh; /* Use viewport height for width */
height: 100vw; /* Use viewport width for height */
max-width: none; /* Remove max-width limitation */
padding: 20px;
box-sizing: border-box;
overflow: visible; /* Show all content */
background-color: transparent; /* Remove background color to show background image */
left: 50%;
top: 50%;
margin-left: -50vh; /* Half of width */
margin-top: -50vw; /* Half of height */
}
body.vertical-reverse .config-button {
transform: rotate(-270deg);
position: fixed;
right: 10px;
bottom: 10px; /* Changed from top to bottom */
z-index: 1000;
}
body.vertical-reverse .departure-container {
display: grid;
grid-template-columns: 1fr;
gap: 10px;
width: 100%; /* Ensure full width */
}
/* Mode indicators - using a class instead of pseudo-element */
.mode-indicator {
font-size: 0.7em;
color: #666;
font-weight: normal;
display: inline;
}