Files
baffle-hub/app/javascript/controllers/timeline_controller.js

178 lines
5.1 KiB
JavaScript

// Timeline controller for handling timezone conversion and animations
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["row", "time", "bar", "timestamp", "date"]
static values = {
mode: { type: String, default: "timeline" } // "timeline", "events", or "individual"
}
connect() {
if (this.modeValue === "timeline") {
this.maxTotal = this.calculateMaxTotal()
this.updateTimeline()
} else if (this.modeValue === "events") {
this.updateEventsTimes()
} else {
this.updateIndividualTimes()
}
}
calculateMaxTotal() {
const totals = this.rowTargets.map(row => parseInt(row.dataset.total))
return Math.max(...totals, 1)
}
updateTimeline() {
this.rowTargets.forEach((row, index) => {
this.updateRow(row, index)
})
}
updateRow(row, index) {
const timeIso = row.dataset.timeIso
const total = parseInt(row.dataset.total)
const timeElement = this.timeTargets.find(target => target.closest('[data-timeline-target="row"]') === row)
const barElement = this.barTargets.find(target => target.closest('[data-timeline-target="row"]') === row)
// Convert ISO time to local time
const date = new Date(timeIso)
// Determine if we should show date based on time range
const now = new Date()
const timeDiff = now - date
const hoursDiff = timeDiff / (1000 * 60 * 60)
let displayTime
if (hoursDiff > 25) {
// For periods longer than 25 hours, show date only (no time)
displayTime = date.toLocaleDateString(undefined, {
month: 'short',
day: 'numeric'
})
} else {
// Check if this is midnight UTC data (daily timeline) vs actual time data (hourly timeline)
// Daily timeline: time is at UTC midnight (hours/minutes/seconds = 0)
// Hourly timeline: time has actual hours/minutes
const utcHours = date.getUTCHours()
const utcMinutes = date.getUTCMinutes()
const utcSeconds = date.getUTCSeconds()
if (utcHours === 0 && utcMinutes === 0 && utcSeconds === 0) {
// This is midnight UTC - treat as daily data, show date only
displayTime = date.toLocaleDateString(undefined, {
month: 'short',
day: 'numeric'
})
} else {
// This is actual time data - show time only
displayTime = date.toLocaleTimeString(undefined, {
hour: '2-digit',
minute: '2-digit',
hour12: false
})
}
}
timeElement.textContent = displayTime
timeElement.title = date.toLocaleString(undefined, {
weekday: 'short',
year: 'numeric',
month: 'short',
day: 'numeric',
timeZoneName: 'short'
})
// Animate the bar width with a slight delay for each row
const barWidth = Math.max((total / this.maxTotal) * 100, 5)
setTimeout(() => {
barElement.style.width = `${barWidth}%`
}, 100 + (index * 50))
}
updateEventsTimes() {
this.timestampTargets.forEach(element => {
const iso = element.dataset.iso
if (iso) {
this.convertToLocalTime(element, iso, "time")
}
})
this.dateTargets.forEach(element => {
const iso = element.dataset.iso
if (iso) {
this.convertToLocalTime(element, iso, "date")
}
})
}
updateIndividualTimes() {
const iso = this.element.dataset.iso
if (iso) {
this.convertToLocalTime(this.element, iso, this.element.dataset.format || "both")
}
}
convertToLocalTime(element, isoString, format) {
const date = new Date(isoString)
switch (format) {
case "time":
element.textContent = date.toLocaleTimeString(undefined, {
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
})
element.title = date.toLocaleString(undefined, {
weekday: 'short',
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
timeZoneName: 'short'
})
break
case "date":
element.textContent = date.toLocaleDateString(undefined, {
year: 'numeric',
month: '2-digit',
day: '2-digit'
})
element.title = date.toLocaleString(undefined, {
weekday: 'short',
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
timeZoneName: 'short'
})
break
case "both":
element.textContent = date.toLocaleString(undefined, {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
})
element.title = date.toLocaleString(undefined, {
weekday: 'short',
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
timeZoneName: 'short'
})
break
}
}
}