diff --git a/components/sidebar.tsx b/components/sidebar.tsx
index ecaad143..cb148a72 100644
--- a/components/sidebar.tsx
+++ b/components/sidebar.tsx
@@ -1,16 +1,15 @@
-import { useState, useEffect, useRef } from "react"
-import type { NextPage } from "next"
-import { loginState, workspacestate } from "@/state"
-import { themeState } from "@/state/theme"
-import { useRecoilState } from "recoil"
-import { Menu, Listbox, Dialog } from "@headlessui/react"
-import { useRouter } from "next/router"
+import { useState, useEffect, useRef } from "react";
+import type { NextPage } from "next";
+import { loginState, workspacestate } from "@/state";
+import { themeState } from "@/state/theme";
+import { useRecoilState } from "recoil";
+import { Menu, Listbox } from "@headlessui/react";
+import { useRouter } from "next/router";
import {
IconHome,
IconHomeFilled,
IconMessage2,
IconMessage2Filled,
- IconServer,
IconClipboardList,
IconClipboardListFilled,
IconBell,
@@ -23,94 +22,32 @@ import {
IconFileText,
IconFileTextFilled,
IconShield,
+ IconShieldFilled,
IconCheck,
IconRosetteDiscountCheck,
IconRosetteDiscountCheckFilled,
IconChevronLeft,
- IconMenu2,
IconSun,
IconMoon,
- IconX,
+ IconLogout,
IconClock,
IconClockFilled,
- IconTrophy,
- IconTrophyFilled,
- IconShieldFilled,
IconTarget,
- IconCopyright,
- IconBook,
- IconBrandGithub,
- IconHistory,
- IconBug,
-} from "@tabler/icons-react"
-import axios from "axios"
-import clsx from "clsx"
-import Parser from "rss-parser"
-import ReactMarkdown from "react-markdown";
-import packageJson from "../package.json";
+ IconDots,
+} from "@tabler/icons-react";
+import axios from "axios";
+import clsx from "clsx";
interface SidebarProps {
- isCollapsed: boolean
- setIsCollapsed: (value: boolean) => void
+ isCollapsed: boolean;
+ setIsCollapsed: (value: boolean) => void;
}
-const ChangelogContent: React.FC<{ workspaceId: number }> = ({ workspaceId }) => {
- const [entries, setEntries] = useState<
- { title: string; link: string; pubDate: string; content: string }[]
- >([]);
- const [loading, setLoading] = useState(true);
-
- useEffect(() => {
- fetch('/api/changelog')
- .then(res => res.json())
- .then(data => {
- setEntries(data);
- setLoading(false);
- })
- .catch(() => setLoading(false));
- }, [workspaceId]);
-
- if (loading) return
Loading...
;
- if (!entries.length) return No entries found.
;
-
- return (
-
- {entries.map((entry, idx) => (
-
- ))}
-
- );
-};
-
const Sidebar: NextPage = ({ isCollapsed, setIsCollapsed }) => {
- const [login, setLogin] = useRecoilState(loginState)
- const [workspace, setWorkspace] = useRecoilState(workspacestate)
- const [theme, setTheme] = useRecoilState(themeState)
- const [showOrbitInfo, setShowOrbitInfo] = useState(false);
- const [showCopyright, setShowCopyright] = useState(false);
- const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false)
- const [showChangelog, setShowChangelog] = useState(false);
- const [changelog, setChangelog] = useState<{ title: string, link: string, pubDate: string, content: string }[]>([]);
- const [changelogLoading, setChangelogLoading] = useState(false);
+ const [login, setLogin] = useRecoilState(loginState);
+ const [workspace, setWorkspace] = useRecoilState(workspacestate);
+ const [theme, setTheme] = useRecoilState(themeState);
+ const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const [docsEnabled, setDocsEnabled] = useState(false);
const [alliesEnabled, setAlliesEnabled] = useState(false);
const [sessionsEnabled, setSessionsEnabled] = useState(false);
@@ -118,65 +55,70 @@ const Sidebar: NextPage = ({ isCollapsed, setIsCollapsed }) => {
const [policiesEnabled, setPoliciesEnabled] = useState(false);
const [pendingPolicyCount, setPendingPolicyCount] = useState(0);
const [pendingNoticesCount, setPendingNoticesCount] = useState(0);
+ const [mobileMoreOpen, setMobileMoreOpen] = useState(false);
+ const [mobileMoreVisible, setMobileMoreVisible] = useState(false);
+ const [isStandalone, setIsStandalone] = useState(false); // Added for PWA check
const workspaceListboxWrapperRef = useRef(null);
- const router = useRouter()
+ const router = useRouter();
+ // Detect PWA Mode
useEffect(() => {
- if (isMobileMenuOpen) {
- document.body.classList.add("overflow-hidden")
- } else {
- document.body.classList.remove("overflow-hidden")
- }
- return () => {
- document.body.classList.remove("overflow-hidden")
+ if (typeof window !== "undefined" && window.matchMedia("(display-mode: standalone)").matches) {
+ setIsStandalone(true);
}
- }, [isMobileMenuOpen])
+ }, []);
+
+ const openMoreSheet = () => {
+ setMobileMoreOpen(true);
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => setMobileMoreVisible(true));
+ });
+ };
+
+ const closeMoreSheet = () => {
+ setMobileMoreVisible(false);
+ setTimeout(() => setMobileMoreOpen(false), 300);
+ };
+
+ useEffect(() => {
+ if (isMobileMenuOpen) document.body.classList.add("overflow-hidden");
+ else document.body.classList.remove("overflow-hidden");
+ return () => document.body.classList.remove("overflow-hidden");
+ }, [isMobileMenuOpen]);
const pages: {
- name: string
- href: string
- icon: React.ElementType
- filledIcon?: React.ElementType
- accessible?: boolean
+ name: string;
+ href: string;
+ icon: React.ElementType;
+ filledIcon?: React.ElementType;
+ accessible?: boolean;
}[] = [
{ name: "Home", href: `/workspace/${workspace.groupId}`, icon: IconHome, filledIcon: IconHomeFilled },
{ name: "Wall", href: `/workspace/${workspace.groupId}/wall`, icon: IconMessage2, filledIcon: IconMessage2Filled, accessible: workspace.yourPermission.includes("view_wall") },
{ name: "Activity", href: `/workspace/${workspace.groupId}/activity`, icon: IconClipboardList, filledIcon: IconClipboardListFilled, accessible: true },
{ name: "Quotas", href: `/workspace/${workspace.groupId}/quotas`, icon: IconTarget, accessible: true },
- ...(noticesEnabled ? [{
- name: "Notices",
- href: `/workspace/${workspace.groupId}/notices`,
- icon: IconClock,
- filledIcon: IconClockFilled,
- accessible: true,
- }] : []),
- ...(alliesEnabled ? [{
- name: "Alliances",
- href: `/workspace/${workspace.groupId}/alliances`,
- icon: IconRosetteDiscountCheck,
- filledIcon: IconRosetteDiscountCheckFilled,
- accessible: true,
- }] : []),
- ...(sessionsEnabled ? [{
- name: "Sessions",
- href: `/workspace/${workspace.groupId}/sessions`,
- icon: IconBell,
- filledIcon: IconBellFilled,
- accessible: true,
- }] : []),
+ ...(noticesEnabled ? [{ name: "Notices", href: `/workspace/${workspace.groupId}/notices`, icon: IconClock, filledIcon: IconClockFilled, accessible: true }] : []),
+ ...(alliesEnabled ? [{ name: "Alliances", href: `/workspace/${workspace.groupId}/alliances`, icon: IconRosetteDiscountCheck, filledIcon: IconRosetteDiscountCheckFilled, accessible: true }] : []),
+ ...(sessionsEnabled ? [{ name: "Sessions", href: `/workspace/${workspace.groupId}/sessions`, icon: IconBell, filledIcon: IconBellFilled, accessible: true }] : []),
{ name: "Staff", href: `/workspace/${workspace.groupId}/views`, icon: IconUser, filledIcon: IconUserFilled, accessible: workspace.yourPermission.includes("view_members") },
...(docsEnabled ? [{ name: "Docs", href: `/workspace/${workspace.groupId}/docs`, icon: IconFileText, filledIcon: IconFileTextFilled, accessible: true }] : []),
...(policiesEnabled ? [{ name: "Policies", href: `/workspace/${workspace.groupId}/policies`, icon: IconShield, filledIcon: IconShieldFilled, accessible: true }] : []),
- { name: "Settings", href: `/workspace/${workspace.groupId}/settings`, icon: IconSettings, filledIcon: IconSettingsFilled, accessible: ["admin", "workspace_customisation", "reset_activity", "manage_features", "manage_apikeys", "view_audit_logs"].some(perm => workspace.yourPermission.includes(perm)) },
+ { name: "Settings", href: `/workspace/${workspace.groupId}/settings`, icon: IconSettings, filledIcon: IconSettingsFilled, accessible: ["admin", "workspace_customisation", "reset_activity", "manage_features", "manage_apikeys", "view_audit_logs"].some((perm) => workspace.yourPermission.includes(perm)) },
];
+ const visiblePages = pages.filter((p) => p.accessible === undefined || p.accessible);
+
+ const bottomBarPages = visiblePages.slice(0, 4);
+ const morePages = visiblePages.slice(4);
+
const gotopage = (page: string) => {
- router.push(page)
- setIsMobileMenuOpen(false)
- }
+ router.push(page);
+ setIsMobileMenuOpen(false);
+ closeMoreSheet();
+ };
const logout = async () => {
- await axios.post("/api/auth/logout")
+ await axios.post("/api/auth/logout");
setLogin({
userId: 1,
username: "",
@@ -185,40 +127,25 @@ const Sidebar: NextPage = ({ isCollapsed, setIsCollapsed }) => {
thumbnail: "",
workspaces: [],
isOwner: false,
- })
- router.push("/login")
- }
+ });
+ router.push("/login");
+ };
const toggleTheme = () => {
- const newTheme = theme === "dark" ? "light" : "dark"
- setTheme(newTheme)
- if (typeof window !== "undefined") {
- localStorage.setItem("theme", newTheme)
- }
- }
-
- useEffect(() => {
- if (!showChangelog) return;
- setChangelogLoading(true);
- fetch('/api/changelog')
- .then(res => res.json())
- .then((data) => {
- const items = Array.isArray(data) ? data : (data?.items ?? []);
- setChangelog(Array.isArray(items) ? items : []);
- })
- .catch(() => setChangelog([]))
- .finally(() => setChangelogLoading(false));
- }, [showChangelog]);
+ const newTheme = theme === "dark" ? "light" : "dark";
+ setTheme(newTheme);
+ if (typeof window !== "undefined") localStorage.setItem("theme", newTheme);
+ };
useEffect(() => {
fetch(`/api/workspace/${workspace.groupId}/settings/general/configuration`)
- .then(res => res.json())
- .then(data => {
- setDocsEnabled(data.value.guides?.enabled ?? false);
- setAlliesEnabled(data.value.allies?.enabled ?? false);
- setSessionsEnabled(data.value.sessions?.enabled ?? false);
- setNoticesEnabled(data.value.notices?.enabled ?? false);
- setPoliciesEnabled(data.value.policies?.enabled ?? false);
+ .then((res) => res.json())
+ .then((data) => {
+ setDocsEnabled(data.value?.guides?.enabled ?? false);
+ setAlliesEnabled(data.value?.allies?.enabled ?? false);
+ setSessionsEnabled(data.value?.sessions?.enabled ?? false);
+ setNoticesEnabled(data.value?.notices?.enabled ?? false);
+ setPoliciesEnabled(data.value?.policies?.enabled ?? false);
})
.catch(() => setDocsEnabled(false));
}, [workspace.groupId]);
@@ -226,490 +153,413 @@ const Sidebar: NextPage = ({ isCollapsed, setIsCollapsed }) => {
useEffect(() => {
if (policiesEnabled) {
fetch(`/api/workspace/${workspace.groupId}/policies/pending`)
- .then(res => res.json())
- .then(data => {
- if (data.success) {
- setPendingPolicyCount(data.count);
- }
- })
+ .then((res) => res.json())
+ .then((data) => data.success && setPendingPolicyCount(data.count))
.catch(() => setPendingPolicyCount(0));
}
}, [workspace.groupId, policiesEnabled]);
useEffect(() => {
- if (noticesEnabled) {
- if (workspace.yourPermission?.includes("approve_notices") || workspace.yourPermission?.includes("manage_notices") || workspace.yourPermission?.includes("admin")) {
- fetch(`/api/workspace/${workspace.groupId}/activity/notices/count`)
- .then(res => res.json())
- .then(data => {
- if (data.success) {
- setPendingNoticesCount(data.count || 0);
- }
- })
- .catch(() => setPendingNoticesCount(0));
- }
+ if (noticesEnabled && (workspace.yourPermission?.includes("approve_notices") || workspace.yourPermission?.includes("manage_notices") || workspace.yourPermission?.includes("admin"))) {
+ fetch(`/api/workspace/${workspace.groupId}/activity/notices/count`)
+ .then((res) => res.json())
+ .then((data) => data.success && setPendingNoticesCount(data.count || 0))
+ .catch(() => setPendingNoticesCount(0));
}
}, [workspace.groupId, noticesEnabled, workspace.yourPermission]);
return (
<>
- {!isMobileMenuOpen && (
-
- )}
-
- {isMobileMenuOpen && (
- setIsMobileMenuOpen(false)}
- />
- )}
-
-
+
-