-
+
Team Schedule
{/* Create button — styled like new-chat-btn */}
@@ -550,9 +577,12 @@ export default function SchedulePage({ isToolManager, isMobile }) {
{/* Mini calendar */}
-
+
+
Filter Events
{setSelDate(d);setPanel('calendar');}} eventDates={eventDates}/>
+
+
)}
@@ -637,7 +667,11 @@ export default function SchedulePage({ isToolManager, isMobile }) {
isToolManager={isToolManager}
onClose={() => setDetailEvent(null)}
onEdit={() => { setEditingEvent(detailEvent); setPanel('eventForm'); setDetailEvent(null); }}
- onAvailabilityChange={() => openDetail(detailEvent)}
+ onAvailabilityChange={(resp) => {
+ // Update the list so the "awaiting response" dot disappears immediately
+ setEvents(prev => prev.map(e => e.id === detailEvent.id ? {...e, my_response: resp} : e));
+ openDetail(detailEvent);
+ }}
/>
)}
diff --git a/frontend/src/components/Sidebar.jsx b/frontend/src/components/Sidebar.jsx
index fbf269f..d08f913 100644
--- a/frontend/src/components/Sidebar.jsx
+++ b/frontend/src/components/Sidebar.jsx
@@ -5,15 +5,7 @@ import { api, parseTS } from '../utils/api.js';
import { useToast } from '../contexts/ToastContext.jsx';
import Avatar from './Avatar.jsx';
import './Sidebar.css';
-
-function useTheme() {
- const [dark, setDark] = useState(() => localStorage.getItem('jama-theme') === 'dark');
- useEffect(() => {
- document.documentElement.setAttribute('data-theme', dark ? 'dark' : 'light');
- localStorage.setItem('jama-theme', dark ? 'dark' : 'light');
- }, [dark]);
- return [dark, setDark];
-}
+import UserFooter from './UserFooter.jsx';
function useAppSettings() {
const [settings, setSettings] = useState({ app_name: 'jama', logo_url: '', color_avatar_public: '', color_avatar_dm: '' });
@@ -52,14 +44,10 @@ function formatTime(dateStr) {
}
export default function Sidebar({ groups, activeGroupId, onSelectGroup, notifications, unreadGroups = new Map(), onNewChat, onProfile, onUsers, onSettings: onOpenSettings, onBranding, onGroupManager, onGroupsUpdated, isMobile, onAbout, onHelp, onlineUserIds = new Set(), features = {} }) {
- const { user, logout } = useAuth();
+ const { user } = useAuth();
const { connected } = useSocket();
const toast = useToast();
- const [showMenu, setShowMenu] = useState(false);
const settings = useAppSettings();
- const [dark, setDark] = useTheme();
- const menuRef = useRef(null);
- const footerBtnRef = useRef(null);
useEffect(() => {
if (!showMenu) return;
@@ -92,7 +80,6 @@ export default function Sidebar({ groups, activeGroupId, onSelectGroup, notifica
});
const getNotifCount = (groupId) => notifications.filter(n => n.groupId === groupId).length;
- const handleLogout = async () => { await logout(); };
const GroupItem = ({ group }) => {
const notifs = getNotifCount(group.id);
@@ -189,62 +176,7 @@ export default function Sidebar({ groups, activeGroupId, onSelectGroup, notifica
)}
-
-
-
-
-
-
- {showMenu && (
-
-
-
-
-
-
-
- )}
-
+
);
-}
+}
\ No newline at end of file
diff --git a/frontend/src/components/UserFooter.jsx b/frontend/src/components/UserFooter.jsx
new file mode 100644
index 0000000..be917dd
--- /dev/null
+++ b/frontend/src/components/UserFooter.jsx
@@ -0,0 +1,88 @@
+import { useState, useRef, useEffect } from 'react';
+import { useAuth } from '../contexts/AuthContext.jsx';
+import Avatar from './Avatar.jsx';
+
+function useTheme() {
+ const [dark, setDark] = useState(() => localStorage.getItem('jama-theme') === 'dark');
+ useEffect(() => {
+ document.documentElement.setAttribute('data-theme', dark ? 'dark' : 'light');
+ localStorage.setItem('jama-theme', dark ? 'dark' : 'light');
+ }, [dark]);
+ return [dark, setDark];
+}
+
+export default function UserFooter({ onProfile, onHelp, onAbout }) {
+ const { user, logout } = useAuth();
+ const [showMenu, setShowMenu] = useState(false);
+ const [dark, setDark] = useTheme();
+ const menuRef = useRef(null);
+ const btnRef = useRef(null);
+
+ useEffect(() => {
+ if (!showMenu) return;
+ const handler = (e) => {
+ if (menuRef.current && !menuRef.current.contains(e.target) &&
+ btnRef.current && !btnRef.current.contains(e.target)) {
+ setShowMenu(false);
+ }
+ };
+ document.addEventListener('mousedown', handler);
+ return () => document.removeEventListener('mousedown', handler);
+ }, [showMenu]);
+
+ const handleLogout = async () => { await logout(); };
+
+ return (
+