Files
helix-engage/src/components/application/pagination/pagination.tsx
saridsa2 e6b2208077 feat: disposition modal, persistent top bar, pagination, QA fixes
- DispositionModal: single modal for all call endings. Dismissable (agent can resume call).
  Agent clicks End → modal → select reason → hangup + dispose. Caller disconnects → same modal.
- One call screen: CallWidget stripped to ringing notification + auto-redirect to Call Desk.
- Persistent top bar in AppShell: agent status toggle + network indicator on all pages.
- Network indicator always visible (Connected/Unstable/No connection).
- Pagination: Untitled UI PaginationCardDefault on Call History + Appointments (20/page).
- Pinned table headers/footers: sticky column headers, scrollable body, pinned pagination.
  Applied to Call Desk worklist, Call History, Appointments, Call Recordings, Missed Calls.
- "Patient" → "Caller" column label in Call History.
- Offline → Ready toggle enabled.
- Profile status dot reflects Ozonetel state.
- NavAccountCard: popover placement top, View Profile + Account Settings restored.
- WIP pages for /profile and /account-settings.
- Enquiry form PHONE_INQUIRY → PHONE enum fix.
- Force Ready / View Profile / Account Settings removed then restored properly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 14:44:48 +05:30

200 lines
8.8 KiB
TypeScript

import type { FC } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faArrowLeft, faArrowRight } from "@fortawesome/pro-duotone-svg-icons";
import { Button } from "@/components/base/buttons/button";
const ArrowLeft: FC<{ className?: string }> = ({ className }) => <FontAwesomeIcon icon={faArrowLeft} className={className} />;
const ArrowRight: FC<{ className?: string }> = ({ className }) => <FontAwesomeIcon icon={faArrowRight} className={className} />;
import { useBreakpoint } from "@/hooks/use-breakpoint";
import { cx } from "@/utils/cx";
import type { PaginationRootProps } from "./pagination-base";
import { Pagination } from "./pagination-base";
interface PaginationProps extends Partial<Omit<PaginationRootProps, "children">> {
/** Whether the pagination buttons are rounded. */
rounded?: boolean;
}
const PaginationItem = ({ value, rounded, isCurrent }: { value: number; rounded?: boolean; isCurrent: boolean }) => {
return (
<Pagination.Item
value={value}
isCurrent={isCurrent}
className={({ isSelected }) =>
cx(
"flex size-9 cursor-pointer items-center justify-center p-3 text-sm font-medium text-quaternary outline-focus-ring transition duration-100 ease-linear hover:bg-primary_hover hover:text-secondary focus-visible:z-10 focus-visible:bg-primary_hover focus-visible:outline-2 focus-visible:outline-offset-2",
rounded ? "rounded-full" : "rounded-lg",
isSelected && "bg-primary_hover text-secondary",
)
}
>
{value}
</Pagination.Item>
);
};
export const PaginationPageDefault = ({ rounded, page = 1, total = 10, className, ...props }: PaginationProps) => {
const isDesktop = useBreakpoint("md");
return (
<Pagination.Root
{...props}
page={page}
total={total}
className={cx("flex w-full items-center justify-between gap-3 border-t border-secondary pt-4 md:pt-5", className)}
>
<div className="hidden flex-1 justify-start md:flex">
<Pagination.PrevTrigger asChild>
<Button iconLeading={ArrowLeft} color="link-gray" size="sm">
{isDesktop ? "Previous" : undefined}
</Button>
</Pagination.PrevTrigger>
</div>
<Pagination.PrevTrigger asChild className="md:hidden">
<Button iconLeading={ArrowLeft} color="secondary" size="sm">
{isDesktop ? "Previous" : undefined}
</Button>
</Pagination.PrevTrigger>
<Pagination.Context>
{({ pages, currentPage, total }) => (
<>
<div className="hidden justify-center gap-0.5 md:flex">
{pages.map((page, index) =>
page.type === "page" ? (
<PaginationItem key={index} rounded={rounded} {...page} />
) : (
<Pagination.Ellipsis key={index} className="flex size-9 shrink-0 items-center justify-center text-tertiary">
&#8230;
</Pagination.Ellipsis>
),
)}
</div>
<div className="flex justify-center text-sm whitespace-pre text-fg-secondary md:hidden">
Page <span className="font-medium">{currentPage}</span> of <span className="font-medium">{total}</span>
</div>
</>
)}
</Pagination.Context>
<div className="hidden flex-1 justify-end md:flex">
<Pagination.NextTrigger asChild>
<Button iconTrailing={ArrowRight} color="link-gray" size="sm">
{isDesktop ? "Next" : undefined}
</Button>
</Pagination.NextTrigger>
</div>
<Pagination.NextTrigger asChild className="md:hidden">
<Button iconTrailing={ArrowRight} color="secondary" size="sm">
{isDesktop ? "Next" : undefined}
</Button>
</Pagination.NextTrigger>
</Pagination.Root>
);
};
export const PaginationPageMinimalCenter = ({ rounded, page = 1, total = 10, className, ...props }: PaginationProps) => {
const isDesktop = useBreakpoint("md");
return (
<Pagination.Root
{...props}
page={page}
total={total}
className={cx("flex w-full items-center justify-between gap-3 border-t border-secondary pt-4 md:pt-5", className)}
>
<div className="flex flex-1 justify-start">
<Pagination.PrevTrigger asChild>
<Button iconLeading={ArrowLeft} color="secondary" size="sm">
{isDesktop ? "Previous" : undefined}
</Button>
</Pagination.PrevTrigger>
</div>
<Pagination.Context>
{({ pages, currentPage, total }) => (
<>
<div className="hidden justify-center gap-0.5 md:flex">
{pages.map((page, index) =>
page.type === "page" ? (
<PaginationItem key={index} rounded={rounded} {...page} />
) : (
<Pagination.Ellipsis key={index} className="flex size-9 shrink-0 items-center justify-center text-tertiary">
&#8230;
</Pagination.Ellipsis>
),
)}
</div>
<div className="flex justify-center text-sm whitespace-pre text-fg-secondary md:hidden">
Page <span className="font-medium">{currentPage}</span> of <span className="font-medium">{total}</span>
</div>
</>
)}
</Pagination.Context>
<div className="flex flex-1 justify-end">
<Pagination.NextTrigger asChild>
<Button iconTrailing={ArrowRight} color="secondary" size="sm">
{isDesktop ? "Next" : undefined}
</Button>
</Pagination.NextTrigger>
</div>
</Pagination.Root>
);
};
export const PaginationCardDefault = ({ rounded, page = 1, total = 10, ...props }: PaginationProps) => {
const isDesktop = useBreakpoint("md");
return (
<Pagination.Root
{...props}
page={page}
total={total}
className="flex w-full items-center justify-between gap-3 border-t border-secondary px-4 py-3 md:px-6 md:pt-3 md:pb-4"
>
<div className="flex flex-1 justify-start">
<Pagination.PrevTrigger asChild>
<Button iconLeading={ArrowLeft} color="secondary" size="sm">
{isDesktop ? "Previous" : undefined}
</Button>
</Pagination.PrevTrigger>
</div>
<Pagination.Context>
{({ pages, currentPage, total }) => (
<>
<div className="hidden justify-center gap-0.5 md:flex">
{pages.map((page, index) =>
page.type === "page" ? (
<PaginationItem key={index} rounded={rounded} {...page} />
) : (
<Pagination.Ellipsis key={index} className="flex size-9 shrink-0 items-center justify-center text-tertiary">
&#8230;
</Pagination.Ellipsis>
),
)}
</div>
<div className="flex justify-center text-sm whitespace-pre text-fg-secondary md:hidden">
Page <span className="font-medium">{currentPage}</span> of <span className="font-medium">{total}</span>
</div>
</>
)}
</Pagination.Context>
<div className="flex flex-1 justify-end">
<Pagination.NextTrigger asChild>
<Button iconTrailing={ArrowRight} color="secondary" size="sm">
{isDesktop ? "Next" : undefined}
</Button>
</Pagination.NextTrigger>
</div>
</Pagination.Root>
);
};