Files
kandev/apps/web/components/integrations/change-request-list.tsx
T
2026-09-12 10:06:05 +02:00

88 lines
2.6 KiB
TypeScript

"use client";
import type { ReactNode } from "react";
import Link from "@/components/routing/app-link";
import { IconExternalLink } from "@tabler/icons-react";
import { Spinner } from "@kandev/ui/spinner";
export type ChangeRequestListProps = {
loading: boolean;
error: string | null;
emptyMessage: string;
isEmpty: boolean;
children: ReactNode;
};
export function ChangeRequestList({
loading,
error,
emptyMessage,
isEmpty,
children,
}: ChangeRequestListProps) {
let content = <div className="divide-y">{children}</div>;
if (loading) {
content = (
<div className="flex justify-center py-10">
<Spinner />
</div>
);
} else if (error) {
content = <div className="py-10 text-center text-sm text-destructive">{error}</div>;
} else if (isEmpty) {
content = <div className="py-10 text-center text-sm text-muted-foreground">{emptyMessage}</div>;
}
return <div className="overflow-hidden rounded-md border">{content}</div>;
}
export type ChangeRequestRowProps = {
stateIcon: ReactNode;
title: string;
href: string;
metadata: ReactNode;
taskIndicator?: ReactNode;
action?: ReactNode;
testId?: string;
dataAttributes?: Record<string, string | number>;
};
export function ChangeRequestRow({
stateIcon,
title,
href,
metadata,
taskIndicator,
action,
testId,
dataAttributes,
}: ChangeRequestRowProps) {
return (
<div
className="flex items-start gap-3 px-4 py-3 transition-colors hover:bg-muted/40"
data-testid={testId}
{...dataAttributes}
>
<span className="mt-1 shrink-0">{stateIcon}</span>
<div className="min-w-0 flex-1">
<Link
href={href}
target="_blank"
rel="noopener noreferrer"
title={title}
className="flex min-h-11 min-w-0 cursor-pointer items-center gap-1.5 text-sm font-semibold hover:underline md:min-h-0 [@media(pointer:coarse)]:min-h-11"
>
<span className="min-w-0 wrap-anywhere md:truncate [@media(pointer:coarse)]:whitespace-normal [@media(pointer:coarse)]:overflow-visible">
{title}
</span>
<IconExternalLink aria-hidden="true" className="h-3 w-3 shrink-0 text-muted-foreground" />
</Link>
<div className="mt-0.5 flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1 text-xs text-muted-foreground [&>span]:max-w-full [&>span]:wrap-anywhere [&>span]:whitespace-normal md:[&>span]:whitespace-nowrap [@media(pointer:coarse)]:[&>span]:whitespace-normal">
{metadata}
{taskIndicator}
</div>
</div>
{action ? <div className="shrink-0">{action}</div> : null}
</div>
);
}