| … | … | @@ -678,62 +678,39 @@ async function loadGcalSection () { |
| 678 | 678 | try { |
| 679 | 679 | const status = await api('GET', '/api/gcal/status') |
| 680 | 680 | |
| 681 | | if (status.connected) { |
| 682 | | let html = '<p style="color:var(--success);margin-bottom:0.5rem;">Connected</p>' |
| 683 | | html += '<p style="font-size:0.9rem;color:var(--text-secondary);margin-bottom:1rem;">Monitoring: ' + (status.calendarIds || ['primary']).join(', ') + '</p>' |
| 684 | | html += '<div style="display:flex;gap:0.5rem;flex-wrap:wrap;">' |
| 685 | | html += '<button class="btn btn-secondary" id="gcal-pick-btn">Change Calendars</button>' |
| 686 | | html += '<button class="btn btn-danger" id="gcal-disconnect-btn">Disconnect</button>' |
| 687 | | html += '</div>' |
| 688 | | html += '<div id="gcal-picker" style="display:none;margin-top:1rem;"></div>' |
| 689 | | |
| 690 | | gcalEl.innerHTML = html |
| 681 | if (!status.configured) { |
| 682 | // Server doesn't have Google credentials set up |
| 683 | gcalEl.innerHTML = ` |
| 684 | <p style="color:var(--text-secondary);"> |
| 685 | To enable Google Calendar sync, set <code>GCAL_CLIENT_ID</code> and |
| 686 | <code>GCAL_CLIENT_SECRET</code> environment variables when starting the server. |
| 687 | </p>` |
| 688 | return |
| 689 | } |
| 691 | 690 | |
| 691 | if (status.connected) { |
| 692 | // Connected — show simple status + disconnect |
| 693 | gcalEl.innerHTML = ` |
| 694 | <p><span style="color:var(--success);font-weight:500;">Connected</span> |
| 695 | — your Google Calendar events automatically block booking slots.</p> |
| 696 | <p style="font-size:0.85rem;color:var(--text-secondary);margin:0.5rem 0;"> |
| 697 | Only availability (busy/free) is read. Event details are never accessed. |
| 698 | </p> |
| 699 | <button class="btn btn-danger" id="gcal-disconnect-btn">Disconnect</button> |
| 700 | ` |
| 692 | 701 | document.getElementById('gcal-disconnect-btn').addEventListener('click', async () => { |
| 693 | | if (!confirm('Disconnect Google Calendar? Booking slots will no longer reflect your calendar events.')) return |
| 702 | if (!confirm('Disconnect Google Calendar?')) return |
| 694 | 703 | try { |
| 695 | 704 | await api('DELETE', '/api/gcal/disconnect') |
| 696 | | showToast('Google Calendar disconnected', 'success') |
| 705 | showToast('Disconnected', 'success') |
| 697 | 706 | loadGcalSection() |
| 698 | 707 | } catch (err) { showToast(err.message, 'error') } |
| 699 | 708 | }) |
| 700 | | |
| 701 | | document.getElementById('gcal-pick-btn').addEventListener('click', async () => { |
| 702 | | const picker = document.getElementById('gcal-picker') |
| 703 | | if (picker.style.display !== 'none') { picker.style.display = 'none'; return } |
| 704 | | |
| 705 | | try { |
| 706 | | const calendars = await api('GET', '/api/gcal/calendars') |
| 707 | | const currentIds = status.calendarIds || ['primary'] |
| 708 | | |
| 709 | | let pickerHtml = '<p style="font-size:0.9rem;margin-bottom:0.5rem;">Select calendars to check for conflicts:</p>' |
| 710 | | for (const cal of calendars) { |
| 711 | | const checked = currentIds.includes(cal.id) ? 'checked' : '' |
| 712 | | pickerHtml += `<label style="display:block;margin:0.3rem 0;font-size:0.9rem;"> |
| 713 | | <input type="checkbox" class="gcal-cal-checkbox" value="${esc(cal.id)}" ${checked}> |
| 714 | | ${esc(cal.summary)}${cal.primary ? ' (primary)' : ''} |
| 715 | | </label>` |
| 716 | | } |
| 717 | | pickerHtml += '<button class="btn btn-accent" id="gcal-save-cals" style="margin-top:0.5rem;">Save</button>' |
| 718 | | picker.innerHTML = pickerHtml |
| 719 | | picker.style.display = '' |
| 720 | | |
| 721 | | document.getElementById('gcal-save-cals').addEventListener('click', async () => { |
| 722 | | const checked = Array.from(document.querySelectorAll('.gcal-cal-checkbox:checked')).map(cb => cb.value) |
| 723 | | if (checked.length === 0) { showToast('Select at least one calendar', 'error'); return } |
| 724 | | try { |
| 725 | | await api('PUT', '/api/gcal/calendars', { calendarIds: checked }) |
| 726 | | showToast('Calendars updated', 'success') |
| 727 | | loadGcalSection() |
| 728 | | } catch (err) { showToast(err.message, 'error') } |
| 729 | | }) |
| 730 | | } catch (err) { showToast(err.message, 'error') } |
| 731 | | }) |
| 732 | 709 | } else { |
| 710 | // Not connected — one button to connect |
| 733 | 711 | gcalEl.innerHTML = ` |
| 734 | 712 | <p style="color:var(--text-secondary);margin-bottom:1rem;"> |
| 735 | | Connect your Google Calendar to automatically block times when you have events. |
| 736 | | We only read availability (busy/free) — never event details. |
| 713 | Sync your Google Calendar so clients only see times when you're actually free. |
| 737 | 714 | </p> |
| 738 | 715 | <button class="btn btn-accent" id="gcal-connect-btn">Connect Google Calendar</button> |
| 739 | 716 | ` |
| … | … | @@ -744,8 +721,8 @@ async function loadGcalSection () { |
| 744 | 721 | } catch (err) { showToast(err.message, 'error') } |
| 745 | 722 | }) |
| 746 | 723 | } |
| 747 | | } catch { |
| 748 | | gcalEl.innerHTML = '<p style="color:var(--text-secondary);">Calendar integration not configured. Set GCAL_CLIENT_ID to enable.</p>' |
| 724 | } catch (err) { |
| 725 | gcalEl.innerHTML = '<p style="color:var(--text-secondary);">Could not load calendar status.</p>' |
| 749 | 726 | } |
| 750 | 727 | } |
| 751 | 728 | |