Files
mimic/frontend/src/components/C2CallbackPicker.tsx

99 lines
3.6 KiB
TypeScript
Raw Normal View History

import { useTranslation } from 'react-i18next';
import { extractApiError } from '@/api/client';
import type { C2Callback } from '@/api/types';
interface C2CallbackPickerProps {
callbacks: C2Callback[];
isLoading: boolean;
isError: boolean;
error: unknown;
selectedId: number | null;
onSelect: (id: number) => void;
rowTestId?: string;
}
export function C2CallbackPicker({
callbacks,
isLoading,
isError,
error,
selectedId,
onSelect,
rowTestId = 'c2-callback-row',
}: C2CallbackPickerProps): JSX.Element {
const { t } = useTranslation();
if (isLoading) {
return <p className="text-[14px] text-graphite">{t('state.loading')}</p>;
}
if (isError) {
return (
<p className="text-[14px] text-bloom-deep">
{extractApiError(error, t('state.error.desc'))}
</p>
);
}
if (callbacks.length === 0) {
return <p className="text-[14px] text-graphite">{t('c2.modal.picker.empty')}</p>;
}
return (
<div className="border border-hairline overflow-x-auto">
<table className="w-full text-[14px]">
<thead>
<tr className="bg-cloud border-b border-hairline">
<th className="px-md py-sm text-left font-medium text-ink">{t('c2.modal.picker.col.displayId')}</th>
<th className="px-md py-sm text-left font-medium text-ink">{t('c2.modal.picker.col.active')}</th>
<th className="px-md py-sm text-left font-medium text-ink">{t('c2.modal.picker.col.host')}</th>
<th className="px-md py-sm text-left font-medium text-ink">{t('c2.modal.picker.col.user')}</th>
<th className="px-md py-sm text-left font-medium text-ink">{t('c2.modal.picker.col.domain')}</th>
<th className="px-md py-sm text-left font-medium text-ink">{t('c2.modal.picker.col.lastCheckin')}</th>
</tr>
</thead>
<tbody>
{callbacks.map((cb) => {
const isSelected = selectedId === cb.display_id;
return (
<tr
key={cb.display_id}
data-testid={rowTestId}
onClick={() => onSelect(cb.display_id)}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
onSelect(cb.display_id);
}
}}
tabIndex={0}
role="button"
className={`cursor-pointer border-b border-hairline focus:outline-none focus-visible:ring-2 focus-visible:ring-primary ${
isSelected ? 'bg-primary-soft' : 'hover:bg-cloud'
}`}
>
<td className="px-md py-sm font-mono">{cb.display_id}</td>
<td className="px-md py-sm">
<span
className={`inline-flex items-center rounded-pill px-3 py-[6px] text-[14px] leading-[1.3] font-medium ${
cb.active
? 'bg-primary-soft text-primary-deep'
: 'bg-cloud text-graphite border border-hairline'
}`}
>
{cb.active ? t('c2.modal.picker.status.active') : t('c2.modal.picker.status.inactive')}
</span>
</td>
<td className="px-md py-sm font-mono">{cb.host}</td>
<td className="px-md py-sm font-mono">{cb.user}</td>
<td className="px-md py-sm font-mono">{cb.domain}</td>
<td className="px-md py-sm font-mono">{cb.last_checkin}</td>
</tr>
);
})}
</tbody>
</table>
</div>
);
}