feat: improve calendar layout

This commit is contained in:
chris 2026-02-12 12:45:45 -05:00
parent 0421d840e3
commit b5cf8f4346
5 changed files with 32 additions and 19 deletions

View File

@ -5,5 +5,6 @@ NEXTCLOUD_URL=
NEXTCLOUD_USER= NEXTCLOUD_USER=
NEXTCLOUD_APP_PASSWORD= NEXTCLOUD_APP_PASSWORD=
NEXTCLOUD_CALENDAR_URL= NEXTCLOUD_CALENDAR_URL=
NEXTCLOUD_CALENDAR_EMBED_URL=
publicVapidKey="YOUR_PUBLIC_VAPID_KEY" publicVapidKey="YOUR_PUBLIC_VAPID_KEY"
privateVapidKey="YOUR_PRIVATE_VAPID_KEY" privateVapidKey="YOUR_PRIVATE_VAPID_KEY"

View File

@ -492,6 +492,10 @@ export function attachAdminDashboardListeners() {
document.getElementById('calendar-clear-btn')?.addEventListener('click', handleCalendarSettingsClear); document.getElementById('calendar-clear-btn')?.addEventListener('click', handleCalendarSettingsClear);
document.getElementById('calendar-embed-url')?.addEventListener('input', (e) => updateCalendarPreview(e.target.value.trim())); document.getElementById('calendar-embed-url')?.addEventListener('input', (e) => updateCalendarPreview(e.target.value.trim()));
setupTabbedInterface(); setupTabbedInterface();
const adminContent = document.getElementById('admin-tabs-content');
if (adminContent) {
adminContent.classList.toggle('calendar-full-bleed', lastAdminTab === 'calendar');
}
if (lastAdminTab === 'calendar') renderCalendarView(); if (lastAdminTab === 'calendar') renderCalendarView();
if (lastAdminTab === 'settings') loadCalendarSettings(); if (lastAdminTab === 'settings') loadCalendarSettings();
} }
@ -549,6 +553,7 @@ function setupTabbedInterface() {
panel.classList.add('hidden'); panel.classList.add('hidden');
}); });
document.getElementById(`tab-content-${tabTarget}`).classList.remove('hidden'); document.getElementById(`tab-content-${tabTarget}`).classList.remove('hidden');
contentContainer.classList.toggle('calendar-full-bleed', tabTarget === 'calendar');
if (tabTarget === 'calendar') { if (tabTarget === 'calendar') {
renderCalendarView(); renderCalendarView();
} }

View File

@ -168,7 +168,7 @@ export async function renderEmployeeDashboard() {
</div> </div>
</div> </div>
<div id="tab-content-employee-calendar" class="space-y-4 hidden"> <div id="tab-content-employee-calendar" class="space-y-4 hidden">
<div class="bg-white rounded-xl shadow-md p-6"> <div class="bg-white rounded-xl shadow-md p-4">
<div class="flex flex-wrap justify-between items-center gap-2 mb-4"> <div class="flex flex-wrap justify-between items-center gap-2 mb-4">
<h3 class="text-xl font-bold text-gray-700">Calendar</h3> <h3 class="text-xl font-bold text-gray-700">Calendar</h3>
<a id="employee-calendar-link" class="text-sm text-blue-600 hover:underline hidden" target="_blank" rel="noopener">Open in new tab</a> <a id="employee-calendar-link" class="text-sm text-blue-600 hover:underline hidden" target="_blank" rel="noopener">Open in new tab</a>
@ -211,18 +211,18 @@ export async function renderAdminDashboard() {
const employeesOnly = allUsers.filter(u => u.role === 'employee'); const employeesOnly = allUsers.filter(u => u.role === 'employee');
mainViews.admin.innerHTML = ` mainViews.admin.innerHTML = `
<div class="max-w-6xl mx-auto space-y-4"> <div class="max-w-6xl mx-auto w-full px-2 sm:px-0 space-y-4">
<div class="bg-white rounded-xl shadow-md p-4"> <div class="bg-white rounded-xl shadow-md p-4">
<h2 class="text-2xl font-bold">Admin Dashboard</h2> <h2 class="text-2xl font-bold">Admin Dashboard</h2>
</div> </div>
<div id="admin-tabs-nav" class="flex border-b border-gray-200 bg-white rounded-t-lg px-4"> <div id="admin-tabs-nav" class="flex border-b border-gray-200 bg-white rounded-t-lg px-4 overflow-x-auto whitespace-nowrap -mx-2">
<button data-tab="overview" class="tab-btn active-tab py-3 px-4">Overview</button> <button data-tab="overview" class="tab-btn active-tab py-3 px-4 flex-shrink-0">Overview</button>
<button data-tab="logs" class="tab-btn py-3 px-4">Time Logs</button> <button data-tab="logs" class="tab-btn py-3 px-4 flex-shrink-0">Time Logs</button>
<button data-tab="users" class="tab-btn py-3 px-4">User Management</button> <button data-tab="users" class="tab-btn py-3 px-4 flex-shrink-0">User Management</button>
<button data-tab="calendar" class="tab-btn py-3 px-4">Calendar</button> <button data-tab="calendar" class="tab-btn py-3 px-4 flex-shrink-0">Calendar</button>
<button data-tab="settings" class="tab-btn py-3 px-4">Settings</button> <button data-tab="settings" class="tab-btn py-3 px-4 flex-shrink-0">Settings</button>
</div> </div>
<div id="admin-tabs-content" class="bg-white rounded-b-lg shadow-md p-6"> <div id="admin-tabs-content" class="admin-tabs-content bg-white rounded-b-lg shadow-md p-6">
<div id="tab-content-overview" class="space-y-8"> <div id="tab-content-overview" class="space-y-8">
<div><h3 class="text-xl font-bold text-gray-700 mb-2">Currently Punched In</h3><ul class="border rounded-lg divide-y">${punchedInEntries.map(e => `<li class="flex flex-col items-start space-y-2 p-3 sm:flex-row sm:items-center sm:justify-between sm:space-y-0"><span class="font-medium text-gray-800">${e.username}</span><div class="flex items-center space-x-4"><span class="text-sm text-gray-500">Since: ${utils.formatDateTime(e.punch_in_time)}</span><button class="force-clock-out-btn px-3 py-1 text-xs bg-red-500 text-white rounded whitespace-nowrap" data-userid="${e.user_id}" data-username="${e.username}">Force Clock Out</button></div></li>`).join('') || '<li class="p-4 text-center text-gray-500">None</li>'}</ul></div> <div><h3 class="text-xl font-bold text-gray-700 mb-2">Currently Punched In</h3><ul class="border rounded-lg divide-y">${punchedInEntries.map(e => `<li class="flex flex-col items-start space-y-2 p-3 sm:flex-row sm:items-center sm:justify-between sm:space-y-0"><span class="font-medium text-gray-800">${e.username}</span><div class="flex items-center space-x-4"><span class="text-sm text-gray-500">Since: ${utils.formatDateTime(e.punch_in_time)}</span><button class="force-clock-out-btn px-3 py-1 text-xs bg-red-500 text-white rounded whitespace-nowrap" data-userid="${e.user_id}" data-username="${e.username}">Force Clock Out</button></div></li>`).join('') || '<li class="p-4 text-center text-gray-500">None</li>'}</ul></div>
<div> <div>
@ -330,7 +330,9 @@ export async function renderCalendarView() {
if (res.success && res.data.url) { if (res.success && res.data.url) {
calendarContainer.innerHTML = ` calendarContainer.innerHTML = `
<div class="-mx-6 -my-6">
<iframe src="${res.data.url}" style="width: 100%; height: 80vh; border: none;"></iframe> <iframe src="${res.data.url}" style="width: 100%; height: 80vh; border: none;"></iframe>
</div>
`; `;
} else { } else {
calendarContainer.innerHTML = ` calendarContainer.innerHTML = `

View File

@ -46,6 +46,10 @@ body {
font-weight: 500; font-weight: 500;
} }
.admin-tabs-content.calendar-full-bleed {
padding: 0;
}
/* Sticky Message Box Styles */ /* Sticky Message Box Styles */
#message-box { #message-box {
position: fixed; /* 'Fixes' the element relative to the browser window */ position: fixed; /* 'Fixes' the element relative to the browser window */

View File

@ -1,8 +1,5 @@
//server.js //server.js
require('dotenv').config(); require('dotenv').config();
console.log('--- DIAGNOSTIC TEST ---');
console.log('publicVapidKey loaded as:', process.env.publicVapidKey);
console.log('--- END DIAGNOSTIC TEST ---');
const express = require('express'); const express = require('express');
const sqlite3 = require('sqlite3'); const sqlite3 = require('sqlite3');
const { open } = require('sqlite'); const { open } = require('sqlite');
@ -41,11 +38,15 @@ async function startServer() {
} }
} }
if (publicVapidKey && privateVapidKey) {
webpush.setVapidDetails( webpush.setVapidDetails(
'mailto:utility@beachpartyballoons.com', // A contact email 'mailto:utility@beachpartyballoons.com',
publicVapidKey, publicVapidKey,
privateVapidKey privateVapidKey
); );
} else {
console.warn('Web push disabled: missing PUBLIC_VAPID_KEY or PRIVATE_VAPID_KEY.');
}
async function initializeDatabase() { async function initializeDatabase() {
console.log("Initializing database schema..."); console.log("Initializing database schema...");