js/calendar.js

Code: DEV-5A3019BF Size: 5.8 KB Lines: 107 Path: /home/prodconfig.wenesthosting.com/dev.solargroup.wenest.se/js/calendar.js

Task / Comment

Open report form
// kalender.js - Calendar

// === CALENDAR CLIENT ===
let calEvents = [];

async function loadCalendarEvents() {
    if (!await ensureToken()) { updateAuthUI(); return; }
    const listEl = document.getElementById('calEventsList') || document.getElementById('callbackList');
    try {
        const res = await fetch('/api/calendar-proxy.php', {
            method: 'POST', headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({
                action: 'list', accessToken: gAccessToken,
                timeMin: new Date().toISOString(),
                timeMax: new Date(Date.now() + 30*86400000).toISOString(),
                maxResults: 50
            })
        });
        const data = await res.json();
        if (data.error === 'TOKEN_EXPIRED') { gAccessToken = ''; updateAuthUI(); return; }
        calEvents = data.events || [];
        renderCalendarEvents();
    } catch(e) {}
}

function renderCalendarEvents() {
    const embed = document.getElementById('calEmbed');
    const listEl = document.getElementById('calEventsList');

    // Show event list
    if (listEl) {
        if (calEvents.length === 0) {
            listEl.innerHTML = '<div style="padding:16px;background:#f8fafc;border-radius:10px;color:#94a3b8;text-align:center;font-size:13px">Inga kommande händelser</div>';
        } else {
            const eventColors = ['#16a34a','#3b82f6','#eab308','#a855f7','#ef4444','#06b6d4'];
            listEl.innerHTML = calEvents.slice(0, 20).map((ev, i) => {
                const start = ev.allDay ? ev.start : new Date(ev.start).toLocaleString('sv-SE', {weekday:'short', month:'short', day:'numeric', hour:'2-digit', minute:'2-digit'});
                const color = eventColors[i % eventColors.length];
                return '<div style="display:flex;align-items:center;gap:12px;padding:10px 12px;background:#fff;border-radius:8px;border:1px solid #f1f5f9">'
                    + '<div style="width:8px;height:8px;border-radius:50%;background:' + color + ';flex-shrink:0"></div>'
                    + '<div style="flex:1;min-width:0">'
                    + '<div style="font-size:13px;font-weight:600;color:#1a1a1a;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + escHtml(ev.summary) + '</div>'
                    + '<div style="font-size:12px;color:#64748b">' + start + (ev.location ? ' · ' + escHtml(ev.location) : '') + '</div>'
                    + '</div>'
                    + '<a href="' + (ev.htmlLink || '#') + '" target="_blank" style="font-size:11px;color:#3b82f6;text-decoration:none;white-space:nowrap">Öppna</a>'
                    + '</div>';
            }).join('');
        }
    }

    // Also render in the calendar embed area
    if (embed) {
        embed.style.display = 'block';
        const calendarHtml = buildCalendarView();
        embed.innerHTML = calendarHtml;
    }
}

function buildCalendarView() {
    const now = new Date();
    const year = now.getFullYear();
    const month = now.getMonth();
    const firstDay = new Date(year, month, 1).getDay();
    const daysInMonth = new Date(year, month + 1, 0).getDate();
    const monthNames = ['Januari','Februari','Mars','April','Maj','Juni','Juli','Augusti','September','Oktober','November','December'];
    const dayNames = ['Mån','Tis','Ons','Tor','Fre','Lör','Sön'];
    const startDay = (firstDay + 6) % 7;

    let html = '<div style="padding:16px">';
    html += '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:16px"><h3 style="font-size:18px;font-weight:700">' + monthNames[month] + ' ' + year + '</h3></div>';
    html += '<div style="display:grid;grid-template-columns:repeat(7,1fr);gap:1px;background:#e5e7eb;border:1px solid #e5e7eb;border-radius:8px;overflow:hidden">';
    dayNames.forEach(d => { html += '<div style="padding:8px 4px;text-align:center;font-size:11px;font-weight:600;color:#64748b;background:#f8fafc">' + d + '</div>'; });

    for (let i = 0; i < startDay; i++) html += '<div style="padding:8px;background:#fafafa;min-height:60px"></div>';

    for (let day = 1; day <= daysInMonth; day++) {
        const dateStr = year + '-' + String(month+1).padStart(2,'0') + '-' + String(day).padStart(2,'0');
        const isToday = day === now.getDate();
        const dayEvents = calEvents.filter(e => (e.start || '').startsWith(dateStr));
        html += '<div style="padding:4px 6px;background:' + (isToday ? '#eff6ff' : '#fff') + ';min-height:60px;vertical-align:top">';
        html += '<div style="font-size:12px;font-weight:' + (isToday ? '700' : '500') + ';color:' + (isToday ? '#024550' : '#334155') + ';margin-bottom:2px">' + day + '</div>';
        dayEvents.slice(0, 2).forEach(e => {
            html += '<div style="font-size:10px;padding:1px 4px;background:#dcfce7;border-radius:3px;margin-bottom:1px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#166534" title="' + escHtml(e.summary) + '">' + escHtml(e.summary) + '</div>';
        });
        if (dayEvents.length > 2) html += '<div style="font-size:10px;color:#94a3b8">+' + (dayEvents.length - 2) + ' till</div>';
        html += '</div>';
    }
    html += '</div></div>';
    return html;
}

async function createCalendarEvent(summary, description, location, startTime, endTime) {
    if (!await ensureToken()) return null;
    try {
        const res = await fetch('/api/calendar-proxy.php', {
            method: 'POST', headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({
                action: 'create', accessToken: gAccessToken,
                summary, description, location, startTime, endTime
            })
        });
        const data = await res.json();
        if (data.success) { loadCalendarEvents(); return data.event; }
        throw new Error(data.error);
    } catch(e) { alert('Kunde inte skapa event: ' + e.message); return null; }
}