Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions apps/web/app/(org)/dashboard/Contexts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ type SharedContext = {
sidebarCollapsed: boolean;
upgradeModalOpen: boolean;
setUpgradeModalOpen: (open: boolean) => void;
referClickedState: boolean;
setReferClickedStateHandler: (referClicked: boolean) => void;
};

type ITheme = "light" | "dark";
Expand Down Expand Up @@ -52,6 +54,7 @@ export function DashboardContexts({
anyNewNotifications,
initialTheme,
initialSidebarCollapsed,
referClicked,
}: {
children: React.ReactNode;
organizationData: SharedContext["organizationData"];
Expand All @@ -63,12 +66,14 @@ export function DashboardContexts({
anyNewNotifications: boolean;
initialTheme: ITheme;
initialSidebarCollapsed: boolean;
referClicked: boolean;
}) {
const [theme, setTheme] = useState<ITheme>(initialTheme);
const [sidebarCollapsed, setSidebarCollapsed] = useState(
initialSidebarCollapsed,
);
const [upgradeModalOpen, setUpgradeModalOpen] = useState(false);
const [referClickedState, setReferClickedState] = useState(referClicked);
const pathname = usePathname();

// Calculate user's spaces (both owned and member of)
Expand Down Expand Up @@ -125,13 +130,21 @@ export function DashboardContexts({
document.body.className = "light";
};
}, [theme]);

const toggleSidebarCollapsed = () => {
setSidebarCollapsed(!sidebarCollapsed);
Cookies.set("sidebarCollapsed", !sidebarCollapsed ? "true" : "false", {
expires: 365,
});
};

const setReferClickedStateHandler = (referClicked: boolean) => {
setReferClickedState(referClicked);
Cookies.set("referClicked", referClicked ? "true" : "false", {
expires: 365,
});
};

return (
<ThemeContext.Provider value={{ theme, setThemeHandler }}>
<DashboardContext.Provider
Expand All @@ -150,6 +163,8 @@ export function DashboardContexts({
sidebarCollapsed,
upgradeModalOpen,
setUpgradeModalOpen,
referClickedState,
setReferClickedStateHandler,
}}
>
{children}
Expand Down
4 changes: 2 additions & 2 deletions apps/web/app/(org)/dashboard/_components/Navbar/CapAIBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ const CapAIBox = ({
}}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
className="hidden p-3 mb-6 w-full rounded-xl border transition-colors cursor-pointer md:block hover:bg-gray-2 h-fit border-gray-3"
className="hidden p-3 mb-6 w-[calc(100%-12px)] mx-auto rounded-xl border transition-colors cursor-pointer md:block hover:bg-gray-2 h-fit border-gray-3"
>
<div className="flex justify-between items-center px-3 pb-3 w-full">
<h3 className="text-sm font-medium text-gray-12">Cap AI</h3>
<p className="text-xs text-gray-10">Available now</p>
<p className="text-[11px] text-gray-10">Available now</p>
</div>
<CapAIArt />
<div
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const CapAIDialog = ({ setOpen }: { setOpen: (open: boolean) => void }) => {
<h4 className="text-sm font-medium text-gray-12">
Features include:
</h4>
<ul className="flex flex-wrap gap-2 text-sm text-gray-11">
<ul className="flex flex-wrap gap-2 text-sm text-gray-12">
{[
"Auto-generated titles",
"Recording summaries",
Expand All @@ -65,7 +65,7 @@ const CapAIDialog = ({ setOpen }: { setOpen: (open: boolean) => void }) => {
icon={faWandMagicSparkles}
className="mr-2 mt-0.5 text-blue-11 size-3"
/>
<span>{feature}</span>
<span className="text-gray-12">{feature}</span>
</li>
))}
</ul>
Expand Down
76 changes: 59 additions & 17 deletions apps/web/app/(org)/dashboard/_components/Navbar/Items.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ import {
PopoverTrigger,
} from "@cap/ui";
import { classNames } from "@cap/utils";
import { faBuilding } from "@fortawesome/free-solid-svg-icons";
import {
faBuilding,
faCircleInfo,
faLink,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import clsx from "clsx";
import { AnimatePresence, motion } from "framer-motion";
Expand Down Expand Up @@ -104,13 +108,13 @@ const AdminNavItems = ({ toggleMobileNav }: Props) => {
duration: 0.2,
}}
className={clsx(
"mt-1.5 mx-auto p-2.5 rounded-xl cursor-pointer bg-gray-3",
sidebarCollapsed ? "w-fit" : "w-full",
"mt-1.5 mx-auto rounded-xl cursor-pointer bg-gray-3",
sidebarCollapsed ? "w-fit px-2 py-0.5" : "w-full p-2.5",
)}
>
<div
className={clsx(
"flex items-center cursor-pointer",
"flex flex-col items-center cursor-pointer",
sidebarCollapsed ? "justify-center" : "justify-between",
)}
role="combobox"
Expand All @@ -121,7 +125,7 @@ const AdminNavItems = ({ toggleMobileNav }: Props) => {
"flex items-center",
sidebarCollapsed
? "justify-center w-fit"
: "justify-between w-full",
: "justify-between gap-2.5 w-full",
)}
>
<div className="flex items-center">
Expand All @@ -139,31 +143,69 @@ const AdminNavItems = ({ toggleMobileNav }: Props) => {
) : (
<Avatar
letterClass={clsx(
sidebarCollapsed ? "text-sm" : "text-[11px]",
sidebarCollapsed ? "text-sm" : "text-[13px]",
)}
className={clsx(
"relative flex-shrink-0 mx-auto",
sidebarCollapsed ? "size-6" : "size-5",
sidebarCollapsed ? "size-6" : "size-7",
)}
name={
activeOrg?.organization.name ??
"No organization found"
}
/>
)}
</div>
<div className="flex flex-col flex-1 items-center h-10">
<div className="flex justify-between items-center w-full">
{!sidebarCollapsed && (
<p className="text-sm truncate leading-0 text-gray-12">
{activeOrg?.organization.name ??
"No organization found"}
</p>
)}
{!sidebarCollapsed && (
<ChevronDown
data-state={open ? "open" : "closed"}
className="size-4 transition-transform duration-200 text-gray-10 data-[state=open]:rotate-180"
/>
)}
</div>
{!sidebarCollapsed && (
<p className="ml-2.5 text-sm text-gray-12 truncate">
{activeOrg?.organization.name ??
"No organization found"}
</p>
<Link
href={
activeOrg?.organization.customDomain
? `https://${activeOrg.organization.customDomain}`
: "/dashboard/settings/organization"
}
rel={
activeOrg?.organization.customDomain
? "noopener noreferrer"
: undefined
}
target={
activeOrg?.organization.customDomain
? "_blank"
: "_self"
}
className="flex truncate w-full overflow-hidden flex-1 gap-1.5 items-center self-start"
>
<FontAwesomeIcon
icon={
activeOrg?.organization.customDomain
? faLink
: faCircleInfo
}
className="duration-200 size-3 text-gray-10"
/>
<p className="w-full text-[11px] flex-1 duration-200 truncate leading-0 text-gray-11">
{activeOrg?.organization.customDomain
? activeOrg?.organization.customDomain
: "No custom domain set"}
</p>
</Link>
)}
</div>
{!sidebarCollapsed && (
<ChevronDown
data-state={open ? "open" : "closed"}
className="w-5 h-auto transition-transform duration-200 text-gray-8 data-[state=open]:rotate-180"
/>
)}
</div>
</div>
<PopoverContent
Expand Down
42 changes: 35 additions & 7 deletions apps/web/app/(org)/dashboard/_components/Navbar/Top.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from "@cap/ui";
import { faBell, faMoon, faSun } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useClickAway } from "@uidotdev/usehooks";
import clsx from "clsx";
import { AnimatePresence } from "framer-motion";
Expand All @@ -28,6 +29,7 @@ import {
useRef,
useState,
} from "react";
import { markAsRead } from "@/actions/notifications/mark-as-read";
import Notifications from "@/app/(org)/dashboard/_components/Notifications";
import { UpgradeModal } from "@/components/UpgradeModal";
import { useDashboardContext, useTheme } from "../../Contexts";
Expand All @@ -44,10 +46,12 @@ import type { DownloadIconHandle } from "../AnimatedIcons/Download";
import type { ReferIconHandle } from "../AnimatedIcons/Refer";

const Top = () => {
const { activeSpace, anyNewNotifications } = useDashboardContext();
const { activeSpace, anyNewNotifications, activeOrganization } =
useDashboardContext();
const [toggleNotifications, setToggleNotifications] = useState(false);
const bellRef = useRef<HTMLDivElement>(null);
const { theme, setThemeHandler } = useTheme();
const queryClient = useQueryClient();

const pathname = usePathname();

Expand Down Expand Up @@ -75,6 +79,18 @@ const Top = () => {
},
);

const markAllAsread = useMutation({
mutationFn: () => markAsRead(),
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ["notifications"],
});
},
onError: (error) => {
console.error("Error marking notifications as read:", error);
},
});

return (
<div
className={clsx(
Expand Down Expand Up @@ -112,11 +128,17 @@ const Top = () => {
data-state={toggleNotifications ? "open" : "closed"}
ref={bellRef}
onClick={() => {
if (anyNewNotifications) {
markAllAsread.mutate();
}
setToggleNotifications(!toggleNotifications);
}}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
if (anyNewNotifications) {
markAllAsread.mutate();
}
setToggleNotifications(!toggleNotifications);
}
}}
Expand Down Expand Up @@ -337,18 +359,24 @@ const MenuItem = memo(({ icon, name, href, onClick, iconClassName }: Props) => {

const ReferButton = () => {
const iconRef = useRef<ReferIconHandle>(null);
const { setReferClickedStateHandler, referClickedState } =
useDashboardContext();

return (
<Link href="/dashboard/refer" className="hidden relative lg:block">
{/* Red notification dot with pulse animation */}
<div className="absolute right-0 top-1 z-10">
<div className="relative">
<div className="absolute inset-0 w-2 h-2 bg-red-400 rounded-full opacity-75 animate-ping" />
<div className="relative w-2 h-2 bg-red-400 rounded-full" />
{!referClickedState && (
<div className="absolute right-0 top-1 z-10">
<div className="relative">
<div className="absolute inset-0 w-2 h-2 bg-red-400 rounded-full opacity-75 animate-ping" />
<div className="relative w-2 h-2 bg-red-400 rounded-full" />
</div>
</div>
</div>
)}

<div
onClick={() => {
setReferClickedStateHandler(true);
}}
onMouseEnter={() => {
iconRef.current?.startAnimation();
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ export const CapCard = ({
{!sharedCapCard && onSelectToggle && (
<div
className={clsx(
"absolute top-2 left-2 z-[20] duration-200",
"absolute top-2 left-2 z-[51] duration-200",
isSelected || anyCapSelected || isDropdownOpen
? "opacity-100"
: "group-hover:opacity-100 opacity-0",
Expand Down Expand Up @@ -467,7 +467,7 @@ export const CapCard = ({
className="text-white size-3"
/>
</div>
<p className="text-xs text-center text-white">
<p className="text-[13px] text-center text-white">
Upload failed
</p>
</div>
Expand Down
2 changes: 2 additions & 0 deletions apps/web/app/(org)/dashboard/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export default async function DashboardLayout({

const theme = cookies().get("theme")?.value ?? "light";
const sidebar = cookies().get("sidebarCollapsed")?.value ?? "false";
const referClicked = cookies().get("referClicked")?.value ?? "false";

return (
<UploadingProvider>
Expand All @@ -78,6 +79,7 @@ export default async function DashboardLayout({
initialSidebarCollapsed={sidebar === "true"}
anyNewNotifications={anyNewNotifications}
userPreferences={userPreferences}
referClicked={referClicked === "true"}
>
<div className="dashboard-grid">
<DesktopNav />
Expand Down
Loading