fix: update sidebar components for consistent styling and improved unread item display

- Changed text color for active and archived item counts in AllPrivateChatsSidebar and AllSharedChatsSidebar to use 'text-muted-foreground' for better visibility.
- Replaced Loader2 with a new Spinner component in InboxSidebar for a consistent loading indicator.
- Enhanced unread item count display in InboxSidebar to show zero when no unread items are present, improving user feedback.
- Adjusted styles for MoreHorizontal and ListFilter icons in InboxSidebar to align with the updated design system.
This commit is contained in:
Anish Sarkar 2026-01-21 22:22:28 +05:30
parent 22b2d6e400
commit 8dcdd27d10
4 changed files with 81 additions and 40 deletions

View file

@ -280,7 +280,7 @@ export function AllPrivateChatsSidebar({
<span className="w-full inline-flex items-center justify-center gap-1.5 px-3 py-1.5 rounded-lg hover:bg-muted transition-colors"> <span className="w-full inline-flex items-center justify-center gap-1.5 px-3 py-1.5 rounded-lg hover:bg-muted transition-colors">
<MessageCircleMore className="h-4 w-4" /> <MessageCircleMore className="h-4 w-4" />
<span>Active</span> <span>Active</span>
<span className="inline-flex items-center justify-center min-w-5 h-5 px-1.5 rounded-full bg-primary/20 text-primary text-xs font-medium">{activeCount}</span> <span className="inline-flex items-center justify-center min-w-5 h-5 px-1.5 rounded-full bg-primary/20 text-muted-foreground text-xs font-medium">{activeCount}</span>
</span> </span>
</TabsTrigger> </TabsTrigger>
<TabsTrigger <TabsTrigger
@ -290,7 +290,7 @@ export function AllPrivateChatsSidebar({
<span className="w-full inline-flex items-center justify-center gap-1.5 px-3 py-1.5 rounded-lg hover:bg-muted transition-colors"> <span className="w-full inline-flex items-center justify-center gap-1.5 px-3 py-1.5 rounded-lg hover:bg-muted transition-colors">
<ArchiveIcon className="h-4 w-4" /> <ArchiveIcon className="h-4 w-4" />
<span>Archived</span> <span>Archived</span>
<span className="inline-flex items-center justify-center min-w-5 h-5 px-1.5 rounded-full bg-primary/20 text-primary text-xs font-medium">{archivedCount}</span> <span className="inline-flex items-center justify-center min-w-5 h-5 px-1.5 rounded-full bg-primary/20 text-muted-foreground text-xs font-medium">{archivedCount}</span>
</span> </span>
</TabsTrigger> </TabsTrigger>
</TabsList> </TabsList>
@ -363,7 +363,7 @@ export function AllPrivateChatsSidebar({
{isDeleting ? ( {isDeleting ? (
<Loader2 className="h-3.5 w-3.5 animate-spin" /> <Loader2 className="h-3.5 w-3.5 animate-spin" />
) : ( ) : (
<MoreHorizontal className="h-3.5 w-3.5" /> <MoreHorizontal className="h-3.5 w-3.5 text-muted-foreground" />
)} )}
<span className="sr-only">{t("more_options") || "More options"}</span> <span className="sr-only">{t("more_options") || "More options"}</span>
</Button> </Button>

View file

@ -280,7 +280,7 @@ export function AllSharedChatsSidebar({
<span className="w-full inline-flex items-center justify-center gap-1.5 px-3 py-1.5 rounded-lg hover:bg-muted transition-colors"> <span className="w-full inline-flex items-center justify-center gap-1.5 px-3 py-1.5 rounded-lg hover:bg-muted transition-colors">
<MessageCircleMore className="h-4 w-4" /> <MessageCircleMore className="h-4 w-4" />
<span>Active</span> <span>Active</span>
<span className="inline-flex items-center justify-center min-w-5 h-5 px-1.5 rounded-full bg-primary/20 text-primary text-xs font-medium">{activeCount}</span> <span className="inline-flex items-center justify-center min-w-5 h-5 px-1.5 rounded-full bg-primary/20 text-muted-foreground text-xs font-medium">{activeCount}</span>
</span> </span>
</TabsTrigger> </TabsTrigger>
<TabsTrigger <TabsTrigger
@ -290,7 +290,7 @@ export function AllSharedChatsSidebar({
<span className="w-full inline-flex items-center justify-center gap-1.5 px-3 py-1.5 rounded-lg hover:bg-muted transition-colors"> <span className="w-full inline-flex items-center justify-center gap-1.5 px-3 py-1.5 rounded-lg hover:bg-muted transition-colors">
<ArchiveIcon className="h-4 w-4" /> <ArchiveIcon className="h-4 w-4" />
<span>Archived</span> <span>Archived</span>
<span className="inline-flex items-center justify-center min-w-5 h-5 px-1.5 rounded-full bg-primary/20 text-primary text-xs font-medium">{archivedCount}</span> <span className="inline-flex items-center justify-center min-w-5 h-5 px-1.5 rounded-full bg-primary/20 text-muted-foreground text-xs font-medium">{archivedCount}</span>
</span> </span>
</TabsTrigger> </TabsTrigger>
</TabsList> </TabsList>
@ -363,7 +363,7 @@ export function AllSharedChatsSidebar({
{isDeleting ? ( {isDeleting ? (
<Loader2 className="h-3.5 w-3.5 animate-spin" /> <Loader2 className="h-3.5 w-3.5 animate-spin" />
) : ( ) : (
<MoreHorizontal className="h-3.5 w-3.5" /> <MoreHorizontal className="h-3.5 w-3.5 text-muted-foreground" />
)} )}
<span className="sr-only">{t("more_options") || "More options"}</span> <span className="sr-only">{t("more_options") || "More options"}</span>
</Button> </Button>

View file

@ -11,7 +11,6 @@ import {
History, History,
Inbox, Inbox,
ListFilter, ListFilter,
Loader2,
MoreHorizontal, MoreHorizontal,
RotateCcw, RotateCcw,
Search, Search,
@ -34,6 +33,7 @@ import {
DropdownMenuTrigger, DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"; } from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Spinner } from "@/components/ui/spinner";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import type { InboxItem } from "@/hooks/use-inbox"; import type { InboxItem } from "@/hooks/use-inbox";
@ -168,16 +168,24 @@ export function InboxSidebar({
return items; return items;
}, [currentTabItems, activeFilter, searchQuery]); }, [currentTabItems, activeFilter, searchQuery]);
// Count unread items per tab // Count unread items per tab (filter-aware)
const unreadMentionsCount = useMemo( const unreadMentionsCount = useMemo(() => {
() => mentionItems.filter((item) => !item.read).length, if (activeFilter === "archived") {
[mentionItems] // In archived view, show unread archived items
); return mentionItems.filter((item) => !item.read && item.archived === true).length;
}
// For "all" and "unread" filters, show unread non-archived items
return mentionItems.filter((item) => !item.read && item.archived !== true).length;
}, [mentionItems, activeFilter]);
const unreadStatusCount = useMemo( const unreadStatusCount = useMemo(() => {
() => statusItems.filter((item) => !item.read).length, if (activeFilter === "archived") {
[statusItems] // In archived view, show unread archived items
); return statusItems.filter((item) => !item.read && item.archived === true).length;
}
// For "all" and "unread" filters, show unread non-archived items
return statusItems.filter((item) => !item.read && item.archived !== true).length;
}, [statusItems, activeFilter]);
const handleItemClick = useCallback( const handleItemClick = useCallback(
async (item: InboxItem) => { async (item: InboxItem) => {
@ -283,7 +291,7 @@ export function InboxSidebar({
case "in_progress": case "in_progress":
return ( return (
<div className="h-8 w-8 flex items-center justify-center rounded-full bg-muted"> <div className="h-8 w-8 flex items-center justify-center rounded-full bg-muted">
<Loader2 className="h-4 w-4 text-foreground animate-spin" /> <Spinner size="sm" className="text-foreground" />
</div> </div>
); );
case "completed": case "completed":
@ -363,7 +371,7 @@ export function InboxSidebar({
size="icon" size="icon"
className="h-8 w-8 rounded-full" className="h-8 w-8 rounded-full"
> >
<ListFilter className="h-4 w-4" /> <ListFilter className="h-4 w-4 text-muted-foreground" />
<span className="sr-only">{t("filter") || "Filter"}</span> <span className="sr-only">{t("filter") || "Filter"}</span>
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
@ -413,7 +421,7 @@ export function InboxSidebar({
size="icon" size="icon"
className="h-8 w-8 rounded-full" className="h-8 w-8 rounded-full"
> >
<MoreHorizontal className="h-4 w-4" /> <MoreHorizontal className="h-4 w-4 text-muted-foreground" />
<span className="sr-only">{t("more_options") || "More options"}</span> <span className="sr-only">{t("more_options") || "More options"}</span>
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
@ -466,11 +474,12 @@ export function InboxSidebar({
<span className="w-full inline-flex items-center justify-center gap-1.5 px-3 py-1.5 rounded-lg hover:bg-muted transition-colors"> <span className="w-full inline-flex items-center justify-center gap-1.5 px-3 py-1.5 rounded-lg hover:bg-muted transition-colors">
<AtSign className="h-4 w-4" /> <AtSign className="h-4 w-4" />
<span>{t("mentions") || "Mentions"}</span> <span>{t("mentions") || "Mentions"}</span>
{unreadMentionsCount > 0 && ( <span className={cn(
<span className="inline-flex items-center justify-center min-w-5 h-5 px-1.5 rounded-full bg-primary/20 text-primary text-xs font-medium"> "inline-flex items-center justify-center min-w-5 h-5 px-1.5 rounded-full bg-primary/20 text-muted-foreground text-xs font-medium",
{unreadMentionsCount} unreadMentionsCount === 0 && "invisible"
</span> )}>
)} {unreadMentionsCount || 0}
</span>
</span> </span>
</TabsTrigger> </TabsTrigger>
<TabsTrigger <TabsTrigger
@ -480,11 +489,12 @@ export function InboxSidebar({
<span className="w-full inline-flex items-center justify-center gap-1.5 px-3 py-1.5 rounded-lg hover:bg-muted transition-colors"> <span className="w-full inline-flex items-center justify-center gap-1.5 px-3 py-1.5 rounded-lg hover:bg-muted transition-colors">
<History className="h-4 w-4" /> <History className="h-4 w-4" />
<span>{t("status") || "Status"}</span> <span>{t("status") || "Status"}</span>
{unreadStatusCount > 0 && ( <span className={cn(
<span className="inline-flex items-center justify-center min-w-5 h-5 px-1.5 rounded-full bg-primary/20 text-primary text-xs font-medium"> "inline-flex items-center justify-center min-w-5 h-5 px-1.5 rounded-full bg-primary/20 text-muted-foreground text-xs font-medium",
{unreadStatusCount} unreadStatusCount === 0 && "invisible"
</span> )}>
)} {unreadStatusCount || 0}
</span>
</span> </span>
</TabsTrigger> </TabsTrigger>
</TabsList> </TabsList>
@ -493,7 +503,7 @@ export function InboxSidebar({
<div className="flex-1 overflow-y-auto overflow-x-hidden p-2"> <div className="flex-1 overflow-y-auto overflow-x-hidden p-2">
{loading ? ( {loading ? (
<div className="flex items-center justify-center py-8"> <div className="flex items-center justify-center py-8">
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" /> <Spinner size="md" className="text-muted-foreground" />
</div> </div>
) : filteredItems.length > 0 ? ( ) : filteredItems.length > 0 ? (
<div className="space-y-2"> <div className="space-y-2">
@ -507,7 +517,7 @@ export function InboxSidebar({
<div <div
key={item.id} key={item.id}
className={cn( className={cn(
"group flex items-center gap-3 rounded-lg px-3 py-5 text-sm", "group flex items-center gap-3 rounded-lg px-3 py-2 text-sm h-[72px] overflow-hidden",
"hover:bg-accent hover:text-accent-foreground", "hover:bg-accent hover:text-accent-foreground",
"transition-colors cursor-pointer", "transition-colors cursor-pointer",
isBusy && "opacity-50 pointer-events-none" isBusy && "opacity-50 pointer-events-none"
@ -547,11 +557,7 @@ export function InboxSidebar({
}} }}
disabled={isBusy} disabled={isBusy}
> >
{isMarkingAsRead ? ( <CheckCheck className="h-3.5 w-3.5" />
<Loader2 className="h-3.5 w-3.5 animate-spin" />
) : (
<CheckCheck className="h-3.5 w-3.5" />
)}
<span className="sr-only">{t("mark_as_read") || "Mark as read"}</span> <span className="sr-only">{t("mark_as_read") || "Mark as read"}</span>
</Button> </Button>
)} )}
@ -566,7 +572,7 @@ export function InboxSidebar({
disabled={isArchiving} disabled={isArchiving}
> >
{isArchiving ? ( {isArchiving ? (
<Loader2 className="h-3.5 w-3.5 animate-spin" /> <Spinner size="xs" />
) : isArchived ? ( ) : isArchived ? (
<RotateCcw className="h-3.5 w-3.5" /> <RotateCcw className="h-3.5 w-3.5" />
) : ( ) : (
@ -623,10 +629,10 @@ export function InboxSidebar({
)} )}
disabled={isBusy} disabled={isBusy}
> >
{isBusy ? ( {isArchiving ? (
<Loader2 className="h-3.5 w-3.5 animate-spin" /> <Spinner size="xs" />
) : ( ) : (
<MoreHorizontal className="h-3.5 w-3.5" /> <MoreHorizontal className="h-3.5 w-3.5 text-muted-foreground" />
)} )}
<span className="sr-only"> <span className="sr-only">
{t("more_options") || "More options"} {t("more_options") || "More options"}

View file

@ -0,0 +1,35 @@
import { cn } from "@/lib/utils";
interface SpinnerProps {
/** Size of the spinner */
size?: "xs" | "sm" | "md" | "lg" | "xl";
/** Whether to hide the track behind the spinner arc */
hideTrack?: boolean;
/** Additional classes to apply */
className?: string;
}
const sizeClasses = {
xs: "h-3 w-3 border-[1.5px]",
sm: "h-4 w-4 border-2",
md: "h-6 w-6 border-2",
lg: "h-8 w-8 border-[3px]",
xl: "h-10 w-10 border-4",
};
export function Spinner({ size = "md", hideTrack = false, className }: SpinnerProps) {
return (
<div
role="status"
aria-label="Loading"
className={cn(
"animate-spin rounded-full",
hideTrack ? "border-transparent" : "border-current/20",
"border-t-current",
sizeClasses[size],
className
)}
/>
);
}