// Art Deco Time Manager - Interactive JavaScript
class TimeManager {
constructor() {
this.currentView = 'calendar';
this.timerInterval = null;
this.timerSeconds = 0;
this.timerRunning = false;
this.selectedDate = new Date();
this.events = this.loadFromStorage('events') || {};
this.schedule = this.loadFromStorage('schedule') || [];
this.deadlines = this.loadFromStorage('deadlines') || [];
this.init();
}
init() {
this.setupNavigationListeners();
this.setupTimeDisplay();
this.setupCalendar();
this.setupTimer();
this.setupSchedule();
this.setupTimezone();
this.setupDeadlines();
this.updateWorldClocks();
setInterval(() => this.updateWorldClocks(), 1000);
}
// Navigation Management
setupNavigationListeners() {
const navTabs = document.querySelectorAll('.nav-tab');
navTabs.forEach(tab => {
tab.addEventListener('click', (e) => {
this.switchView(e.target.dataset.view);
});
});
}
switchView(viewName) {
// Update navigation tabs
document.querySelectorAll('.nav-tab').forEach(tab => {
tab.classList.toggle('active', tab.dataset.view === viewName);
});
// Update view panels
document.querySelectorAll('.view-panel').forEach(panel => {
panel.classList.toggle('active', panel.id === `${viewName}View`);
});
this.currentView = viewName;
}
// Time Display
setupTimeDisplay() {
const updateTime = () => {
const now = new Date();
const timeStr = now.toLocaleTimeString('en-US', {
hour12: false,
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
const dateStr = now.toLocaleDateString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
});
document.getElementById('currentTime').textContent = timeStr;
document.getElementById('currentDate').textContent = dateStr;
};
updateTime();
setInterval(updateTime, 1000);
}
// Calendar Functionality
setupCalendar() {
this.renderCalendar();
document.getElementById('prevMonth').addEventListener('click', () => {
this.selectedDate.setMonth(this.selectedDate.getMonth() - 1);
this.renderCalendar();
});
document.getElementById('nextMonth').addEventListener('click', () => {
this.selectedDate.setMonth(this.selectedDate.getMonth() + 1);
this.renderCalendar();
});
}
renderCalendar() {
const year = this.selectedDate.getFullYear();
const month = this.selectedDate.getMonth();
const firstDay = new Date(year, month, 1).getDay();
const daysInMonth = new Date(year, month + 1, 0).getDate();
const daysInPrevMonth = new Date(year, month, 0).getDate();
// Update month/year display
document.getElementById('monthYear').textContent =
new Date(year, month).toLocaleDateString('en-US', {
month: 'long',
year: 'numeric'
});
// Clear existing calendar days
const grid = document.getElementById('calendarGrid');
const existingDays = grid.querySelectorAll('.calendar-day');
existingDays.forEach(day => day.remove());
// Add previous month's trailing days
for (let i = firstDay - 1; i >= 0; i--) {
const dayEl = this.createCalendarDay(
daysInPrevMonth - i,
month - 1,
year,
true
);
grid.appendChild(dayEl);
}
// Add current month's days
for (let day = 1; day <= daysInMonth; day++) {
const dayEl = this.createCalendarDay(day, month, year, false);
grid.appendChild(dayEl);
}
// Add next month's leading days
const totalCells = grid.children.length - 7; // Subtract header row
const remainingCells = 42 - totalCells; // 6 weeks * 7 days
for (let day = 1; day <= remainingCells; day++) {
const dayEl = this.createCalendarDay(day, month + 1, year, true);
grid.appendChild(dayEl);
}
this.renderEvents();
}
createCalendarDay(day, month, year, isOtherMonth) {
const dayEl = document.createElement('div');
dayEl.className = 'calendar-day';
if (isOtherMonth) dayEl.classList.add('other-month');
const date = new Date(year, month, day);
const today = new Date();
if (this.isSameDay(date, today)) {
dayEl.classList.add('today');
}
dayEl.textContent = day;
dayEl.addEventListener('click', () => this.selectDate(date));
// Check for events
const dateKey = this.getDateKey(date);
if (this.events[dateKey]) {
dayEl.style.borderBottom = '3px solid var(--deco-gold)';
}
return dayEl;
}
selectDate(date) {
this.selectedDate = date;
this.renderEvents();
}
renderEvents() {
const eventsList = document.getElementById('eventsList');
eventsList.innerHTML = '';
const dateKey = this.getDateKey(this.selectedDate);
const dayEvents = this.events[dateKey] || [];
if (dayEvents.length === 0) {
eventsList.innerHTML = '
No appointments scheduled';
return;
}
dayEvents.forEach(event => {
const li = document.createElement('li');
li.className = 'event-item';
li.textContent = event;
eventsList.appendChild(li);
});
}
// Timer Functionality
setupTimer() {
document.getElementById('startTimer').addEventListener('click', () => this.startTimer());
document.getElementById('pauseTimer').addEventListener('click', () => this.pauseTimer());
document.getElementById('resetTimer').addEventListener('click', () => this.resetTimer());
// Preset buttons
document.querySelectorAll('.preset-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
const minutes = parseInt(e.target.dataset.minutes);
this.timerSeconds = minutes * 60;
this.updateTimerDisplay();
});
});
}
startTimer() {
if (!this.timerRunning) {
this.timerRunning = true;
this.timerInterval = setInterval(() => {
if (this.timerSeconds > 0) {
this.timerSeconds--;
this.updateTimerDisplay();
} else {
this.pauseTimer();
this.showNotification('Timer Complete!');
}
}, 1000);
}
}
pauseTimer() {
this.timerRunning = false;
if (this.timerInterval) {
clearInterval(this.timerInterval);
this.timerInterval = null;
}
}
resetTimer() {
this.pauseTimer();
this.timerSeconds = 0;
this.updateTimerDisplay();
}
updateTimerDisplay() {
const hours = Math.floor(this.timerSeconds / 3600);
const minutes = Math.floor((this.timerSeconds % 3600) / 60);
const seconds = this.timerSeconds % 60;
const display = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
document.getElementById('timerDisplay').textContent = display;
}
// Schedule Functionality
setupSchedule() {
const form = document.getElementById('scheduleForm');
form.addEventListener('submit', (e) => {
e.preventDefault();
this.addScheduleItem();
});
this.renderSchedule();
}
addScheduleItem() {
const time = document.getElementById('scheduleTime').value;
const task = document.getElementById('scheduleTask').value;
if (time && task) {
this.schedule.push({ time, task });
this.schedule.sort((a, b) => a.time.localeCompare(b.time));
this.saveToStorage('schedule', this.schedule);
this.renderSchedule();
// Reset form
document.getElementById('scheduleForm').reset();
}
}
renderSchedule() {
const timeline = document.getElementById('scheduleTimeline');
timeline.innerHTML = '';
if (this.schedule.length === 0) {
timeline.innerHTML = 'No appointments scheduled
';
return;
}
this.schedule.forEach((item, index) => {
const itemEl = document.createElement('div');
itemEl.className = 'timeline-item';
itemEl.innerHTML = `
${item.time}
${item.task}
`;
timeline.appendChild(itemEl);
});
}
// Timezone Functionality
setupTimezone() {
document.getElementById('convertTime').addEventListener('click', () => {
this.convertTime();
});
}
updateWorldClocks() {
const cards = document.querySelectorAll('.timezone-card');
cards.forEach(card => {
const timezone = card.dataset.tz;
const timeEl = card.querySelector('.tz-time');
const time = new Date().toLocaleTimeString('en-US', {
timeZone: timezone,
hour12: false,
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
timeEl.textContent = time;
});
}
convertTime() {
const time = document.getElementById('converterTime').value;
const fromTz = document.getElementById('fromTimezone').value;
const results = document.getElementById('converterResults');
if (!time) {
results.innerHTML = 'Please select a time to convert
';
return;
}
const [hours, minutes] = time.split(':').map(Number);
const date = new Date();
date.setHours(hours, minutes, 0, 0);
const zones = [
{ name: 'New York', tz: 'America/New_York' },
{ name: 'London', tz: 'Europe/London' },
{ name: 'Paris', tz: 'Europe/Paris' },
{ name: 'Tokyo', tz: 'Asia/Tokyo' }
];
results.innerHTML = 'Converted Times:
';
zones.forEach(zone => {
const convertedTime = date.toLocaleTimeString('en-US', {
timeZone: zone.tz,
hour12: false,
hour: '2-digit',
minute: '2-digit'
});
results.innerHTML += `${zone.name}: ${convertedTime}
`;
});
}
// Deadlines Functionality
setupDeadlines() {
const form = document.getElementById('deadlineForm');
form.addEventListener('submit', (e) => {
e.preventDefault();
this.addDeadline();
});
this.renderDeadlines();
setInterval(() => this.updateDeadlineCountdowns(), 60000); // Update every minute
}
addDeadline() {
const title = document.getElementById('deadlineTitle').value;
const date = document.getElementById('deadlineDate').value;
const priority = document.getElementById('deadlinePriority').value;
if (title && date) {
const deadline = {
id: Date.now(),
title,
date: new Date(date),
priority
};
this.deadlines.push(deadline);
this.deadlines.sort((a, b) => a.date - b.date);
this.saveToStorage('deadlines', this.deadlines);
this.renderDeadlines();
// Reset form
document.getElementById('deadlineForm').reset();
}
}
renderDeadlines() {
const list = document.getElementById('deadlinesList');
list.innerHTML = '';
if (this.deadlines.length === 0) {
list.innerHTML = 'No deadlines set
';
return;
}
this.deadlines.forEach(deadline => {
const itemEl = document.createElement('div');
itemEl.className = `deadline-item ${deadline.priority}-priority`;
const countdown = this.getCountdown(deadline.date);
itemEl.innerHTML = `
${deadline.date.toLocaleString()}
${countdown}
`;
const removeBtn = itemEl.querySelector('.deadline-remove');
removeBtn.addEventListener('click', () => this.removeDeadline(deadline.id));
list.appendChild(itemEl);
});
}
removeDeadline(id) {
this.deadlines = this.deadlines.filter(d => d.id !== id);
this.saveToStorage('deadlines', this.deadlines);
this.renderDeadlines();
}
updateDeadlineCountdowns() {
if (this.currentView === 'deadlines') {
this.renderDeadlines();
}
}
getCountdown(targetDate) {
const now = new Date();
const diff = targetDate - now;
if (diff < 0) {
return 'Overdue';
}
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
if (days > 0) {
return `${days} days, ${hours} hours remaining`;
} else if (hours > 0) {
return `${hours} hours, ${minutes} minutes remaining`;
} else {
return `${minutes} minutes remaining`;
}
}
// Utility Functions
isSameDay(date1, date2) {
return date1.getFullYear() === date2.getFullYear() &&
date1.getMonth() === date2.getMonth() &&
date1.getDate() === date2.getDate();
}
getDateKey(date) {
return `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`;
}
showNotification(message) {
// Create a temporary notification
const notification = document.createElement('div');
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
padding: 20px;
background: var(--deco-gold);
color: var(--deco-black);
border: 2px solid var(--deco-black);
font-family: var(--font-accent);
text-transform: uppercase;
letter-spacing: 0.1rem;
z-index: 1000;
animation: slideIn 0.5s ease;
`;
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => {
notification.style.animation = 'slideOut 0.5s ease';
setTimeout(() => notification.remove(), 500);
}, 3000);
}
// Storage Functions
saveToStorage(key, data) {
try {
localStorage.setItem(`timeManager_${key}`, JSON.stringify(data));
} catch (e) {
console.error('Failed to save to storage:', e);
}
}
loadFromStorage(key) {
try {
const data = localStorage.getItem(`timeManager_${key}`);
if (data) {
const parsed = JSON.parse(data);
// Convert date strings back to Date objects for deadlines
if (key === 'deadlines') {
return parsed.map(d => ({
...d,
date: new Date(d.date)
}));
}
return parsed;
}
} catch (e) {
console.error('Failed to load from storage:', e);
}
return null;
}
}
// Add animations to stylesheet dynamically
const style = document.createElement('style');
style.textContent = `
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideOut {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(100%);
opacity: 0;
}
}
`;
document.head.appendChild(style);
// Initialize Time Manager when DOM is ready
document.addEventListener('DOMContentLoaded', () => {
new TimeManager();
});