feat(sessions): merge the activity feed into a tab on /sessions
Sessions and Activity Feed are two views of the same event pipeline, so they now share one page: /sessions renders a tab bar (Sessions | Activity Feed) with the active view in the ?view= query param, and /activity redirects to /sessions?view=events. The sidebar entry is dropped. The tab bar itself moves out of SettingsHub into components/PageTabs so both hubs share it, and DocumentTitle loses the two branches for paths that now only redirect.
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* @file PageTabs.tsx
|
||||
* @description Shared top-of-page tab bar used by the pages that merged several
|
||||
* former routes into one (Settings hub, Sessions/Events history). Rendering only -
|
||||
* the owning page keeps the active tab in its own query param.
|
||||
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
||||
*/
|
||||
import type { ComponentType } from "react";
|
||||
|
||||
export interface PageTab {
|
||||
id: string;
|
||||
label: string;
|
||||
Icon: ComponentType<{ className?: string }>;
|
||||
/** Small muted suffix, e.g. "soon" for a tool that is not integrated yet. */
|
||||
badge?: string;
|
||||
}
|
||||
|
||||
interface PageTabsProps {
|
||||
tabs: PageTab[];
|
||||
activeId: string;
|
||||
onSelect: (id: string) => void;
|
||||
ariaLabel: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Horizontal tab strip for a page that hosts several panels.
|
||||
* @param props - Tabs to render, the active tab id, and the selection callback.
|
||||
* @returns The tab bar element.
|
||||
*/
|
||||
export function PageTabs({ tabs, activeId, onSelect, ariaLabel }: PageTabsProps) {
|
||||
return (
|
||||
<div
|
||||
role="tablist"
|
||||
aria-label={ariaLabel}
|
||||
className="flex items-center gap-1.5 overflow-x-auto no-scrollbar border-b border-border/60 pb-2"
|
||||
>
|
||||
{tabs.map(({ id, label, Icon, badge }) => {
|
||||
const isActive = id === activeId;
|
||||
return (
|
||||
<button
|
||||
key={id}
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-selected={isActive}
|
||||
onClick={() => onSelect(id)}
|
||||
className={`inline-flex items-center gap-1.5 text-xs whitespace-nowrap px-3 py-1.5 rounded-lg border transition-colors flex-shrink-0 ${
|
||||
isActive
|
||||
? "bg-accent/15 border-accent/30 text-accent"
|
||||
: "border-border text-fg-secondary hover:text-fg-primary hover:bg-surface-3"
|
||||
}`}
|
||||
>
|
||||
<Icon className="w-3.5 h-3.5" />
|
||||
{label}
|
||||
{badge && <span className="text-[10px] text-fg-muted">{badge}</span>}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user