feat(channels): fix the manager header, scroll only the table rows

Lift the whole subscribed-view header — tabs, sync stats + actions, the
intro, the tag chips and the sync-status chips — into the shell's fixed
band via PageToolbar. Combined with the sticky table header row, only the
channel rows scroll now; everything above them stays put.

The status chips move out of DataTable's controlsLeading into the fixed
chrome (they belong above the header, not in the scrolling controls row).

Note: the DataTable pager (rows-per-page / pagination) stays in the
scroller for now — it rarely shows on the manager (few channels = one
page). The discovery sub-view's chrome is still inline (separate
component, handled in the sweep). Suite 15/15, tsc + knip clean.
This commit is contained in:
2026-07-18 05:15:01 +02:00
parent 7df9bc0035
commit dcadbe346d
+15 -6
View File
@@ -25,6 +25,7 @@ import { subsColumn } from "./channelColumns";
import { notify } from "../lib/notifications";
import Tooltip from "./Tooltip";
import DataTable, { type Column } from "./DataTable";
import { PageToolbar } from "./PageShell";
import ChannelLink from "./ChannelLink";
import ChannelDiscovery from "./ChannelDiscovery";
import TagManager from "./TagManager";
@@ -415,8 +416,12 @@ export default function Channels({
return (
<>
{/* The whole manager header — tabs, stats, description, tags, status chips — is fixed chrome:
it portals into the shell's fixed band, so only the table rows scroll (under the sticky
header row). */}
<PageToolbar>
{tabs}
<div className="px-4 pb-4 pt-3 max-w-7xl mx-auto">
<div className="px-4 pt-3 pb-2 max-w-7xl mx-auto">
{/* Per-user sync status + catalog-wide actions on one row (search/tags filtering
now lives in the table headers). */}
<div className="flex items-start justify-between gap-4 flex-wrap mb-4">
@@ -535,11 +540,16 @@ export default function Channels({
{t("channels.tags.manage")}
</button>
</div>
{tagManagerOpen && (
<TagManager onClose={() => setTagManagerOpen(false)} onFocusChannel={onFocusChannel} />
)}
{/* Sync-status chips — the last band of fixed chrome, right above the table header. */}
{statusChips}
</div>
</PageToolbar>
{tagManagerOpen && (
<TagManager onClose={() => setTagManagerOpen(false)} onFocusChannel={onFocusChannel} />
)}
{/* Channel table */}
<div className="px-4 pb-4 pt-2 max-w-7xl mx-auto">
{/* Channel table — only the rows scroll; the header row is sticky (see DataTable). */}
{channelsQuery.isLoading ? (
<div className="text-muted py-8">{t("channels.loading")}</div>
) : (
@@ -549,7 +559,6 @@ export default function Channels({
rowKey={(c) => c.id}
persistKey={accountKey(LS.channelsTable) ?? undefined}
controlsPosition="top"
controlsLeading={statusChips}
rowClassName={(c) => (c.hidden ? "opacity-60" : "")}
emptyText={t("channels.empty")}
/>