Next.js 16, React 19.2, Identities page, Invitations identities step, PNPM Catalogs (#381)

* Upgraded to Next.js 16
* Refactored code to comply with React 19.2 ESLint rules
* Refactored some useEffect usages with the new useEffectEvent
* Added Identities page and added second step to set up an identity after accepting an invitation
* Updated all dependencies
* Introduced PNPM catalogs for some frequently updated dependencies
* Bugs fixing and improvements
This commit is contained in:
Giancarlo Buomprisco
2025-10-22 11:47:47 +09:00
committed by GitHub
parent ea0c1dde80
commit 2c0d0bf7a1
98 changed files with 4812 additions and 4394 deletions

View File

@@ -1,6 +1,6 @@
'use client';
import { useContext, useId, useRef, useState } from 'react';
import { useContext, useId, useState } from 'react';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
@@ -43,23 +43,20 @@ export function Sidebar(props: {
}) => React.ReactNode);
}) {
const [collapsed, setCollapsed] = useState(props.collapsed ?? false);
const isExpandedRef = useRef<boolean>(false);
const [isExpanded, setIsExpanded] = useState(false);
const expandOnHover =
props.expandOnHover ??
process.env.NEXT_PUBLIC_EXPAND_SIDEBAR_ON_HOVER === 'true';
const sidebarSizeClassName = getSidebarSizeClassName(
collapsed,
isExpandedRef.current,
);
const sidebarSizeClassName = getSidebarSizeClassName(collapsed, isExpanded);
const className = getClassNameBuilder(
cn(props.className ?? '', sidebarSizeClassName, {}),
)();
const containerClassName = cn(sidebarSizeClassName, 'bg-inherit', {
'max-w-[4rem]': expandOnHover && isExpandedRef.current,
'max-w-[4rem]': expandOnHover && isExpanded,
});
const ctx = { collapsed, setCollapsed };
@@ -68,7 +65,7 @@ export function Sidebar(props: {
props.collapsed && expandOnHover
? () => {
setCollapsed(false);
isExpandedRef.current = true;
setIsExpanded(true);
}
: undefined;
@@ -77,11 +74,11 @@ export function Sidebar(props: {
? () => {
if (!isRadixPopupOpen()) {
setCollapsed(true);
isExpandedRef.current = false;
setIsExpanded(false);
} else {
onRadixPopupClose(() => {
setCollapsed(true);
isExpandedRef.current = false;
setIsExpanded(false);
});
}
}
@@ -124,6 +121,66 @@ export function SidebarContent({
return <div className={className}>{children}</div>;
}
function SidebarGroupWrapper({
id,
sidebarCollapsed,
collapsible,
isGroupCollapsed,
setIsGroupCollapsed,
label,
}: {
id: string;
sidebarCollapsed: boolean;
collapsible: boolean;
isGroupCollapsed: boolean;
setIsGroupCollapsed: (isGroupCollapsed: boolean) => void;
label: React.ReactNode;
}) {
const className = cn(
'px-container group flex items-center justify-between space-x-2.5',
{
'py-2.5': !sidebarCollapsed,
},
);
if (collapsible) {
return (
<button
aria-expanded={!isGroupCollapsed}
aria-controls={id}
onClick={() => setIsGroupCollapsed(!isGroupCollapsed)}
className={className}
>
<span
className={'text-muted-foreground text-xs font-semibold uppercase'}
>
{label}
</span>
<If condition={collapsible}>
<ChevronDown
className={cn(`h-3 transition duration-300`, {
'rotate-180': !isGroupCollapsed,
})}
/>
</If>
</button>
);
}
if (sidebarCollapsed) {
return null;
}
return (
<div className={className}>
<span className={'text-muted-foreground text-xs font-semibold uppercase'}>
{label}
</span>
</div>
);
}
export function SidebarGroup({
label,
collapsed = false,
@@ -138,61 +195,20 @@ export function SidebarGroup({
const [isGroupCollapsed, setIsGroupCollapsed] = useState(collapsed);
const id = useId();
const Title = (props: React.PropsWithChildren) => {
if (sidebarCollapsed) {
return null;
}
return (
<span className={'text-muted-foreground text-xs font-semibold uppercase'}>
{props.children}
</span>
);
};
const Wrapper = () => {
const className = cn(
'px-container group flex items-center justify-between space-x-2.5',
{
'py-2.5': !sidebarCollapsed,
},
);
if (collapsible) {
return (
<button
aria-expanded={!isGroupCollapsed}
aria-controls={id}
onClick={() => setIsGroupCollapsed(!isGroupCollapsed)}
className={className}
>
<Title>{label}</Title>
<If condition={collapsible}>
<ChevronDown
className={cn(`h-3 transition duration-300`, {
'rotate-180': !isGroupCollapsed,
})}
/>
</If>
</button>
);
}
return (
<div className={className}>
<Title>{label}</Title>
</div>
);
};
return (
<div
className={cn('flex flex-col', {
'gap-y-2 py-1': !collapsed,
})}
>
<Wrapper />
<SidebarGroupWrapper
id={id}
sidebarCollapsed={sidebarCollapsed}
collapsible={collapsible}
isGroupCollapsed={isGroupCollapsed}
setIsGroupCollapsed={setIsGroupCollapsed}
label={label}
/>
<If condition={collapsible ? !isGroupCollapsed : true}>
<div id={id} className={'flex flex-col space-y-1.5'}>