import { MERCHANT_REPORT_TYPES_MAP } from '@ballerine/common';
import {
Badge,
CheckCircle,
ContentTooltip,
severityToClassName,
TextWithNAFallback,
WarningFilledSvg,
} from '@ballerine/ui';
import { createColumnHelper, RowData } from '@tanstack/react-table';
import dayjs from 'dayjs';
import timezone from 'dayjs/plugin/timezone';
import utc from 'dayjs/plugin/utc';
import { Minus } from 'lucide-react';
import { useMemo } from 'react';
import { titleCase } from 'string-ts';
import { CopyToClipboardButton } from '@/common/components/atoms/CopyToClipboardButton/CopyToClipboardButton';
import { IndicatorCircle } from '@/common/components/atoms/IndicatorCircle/IndicatorCircle';
import {
NO_VIOLATION_DETECTED_RISK_INDICATOR_ID,
POSITIVE_RISK_LEVEL_ID,
} from '@/common/constants';
import { useEllipsesWithTitle } from '@/common/hooks/useEllipsesWithTitle/useEllipsesWithTitle';
import { ctw } from '@/common/utils/ctw/ctw';
import { TBusinessReport } from '@/domains/business-reports/fetchers';
import { MerchantMonitoringReportStatus } from '@/pages/MerchantMonitoring/components/MerchantMonitoringReportStatus/MerchantMonitoringReportStatus';
import { statusToData } from '@/pages/MerchantMonitoring/components/MerchantMonitoringReportStatus/MerchantMonitoringStatusBadge';
import { uniqBy } from 'lodash-es';
dayjs.extend(utc);
dayjs.extend(timezone);
// https://tanstack.com/table/v8/docs/api/core/column-def#meta
declare module '@tanstack/react-table' {
interface ColumnMeta {
conditional?: true;
showColumn?: boolean;
}
}
const columnHelper = createColumnHelper();
const SCAN_TYPES = {
ONBOARDING: 'Onboarding',
MONITORING: 'Monitoring',
} as const;
const REPORT_TYPE_TO_SCAN_TYPE = {
[MERCHANT_REPORT_TYPES_MAP.MERCHANT_REPORT_T1]: SCAN_TYPES.ONBOARDING,
[MERCHANT_REPORT_TYPES_MAP.ONGOING_MERCHANT_REPORT_T1]: SCAN_TYPES.MONITORING,
} as const;
export const useColumns = ({ isDemoAccount = false }) => {
return useMemo(() => {
const columns = [
columnHelper.accessor('companyName', {
cell: info => {
const companyName = info.getValue();
const isExample = info.row.original.isExample;
return (
{companyName}
{isExample && (
example
)}
);
},
header: 'Company Name',
}),
columnHelper.accessor('website', {
cell: ({ getValue }) => {
const website = getValue()
.replace(/(^\w+:|^)\/\//, '')
.replace(/\/$/, '');
return (
{website}
);
},
header: 'Website',
}),
columnHelper.accessor('riskLevel', {
cell: info => {
const riskLevel = info.getValue();
return (
{riskLevel ? (
{titleCase(riskLevel)}
) : (
)}
);
},
header: () => Risk Level
,
}),
columnHelper.accessor('data.allViolations', {
cell: ({ row }) => {
let violations = (row.original.data?.allViolations ?? [])
.filter(
violation =>
violation.id !== NO_VIOLATION_DETECTED_RISK_INDICATOR_ID &&
violation.name &&
violation.riskLevel &&
violation.riskLevel !== POSITIVE_RISK_LEVEL_ID,
)
.sort((a, b) => {
return a.riskLevel === b.riskLevel
? (a.name ?? '').localeCompare(b.name ?? '')
: (a.riskLevel ?? '').localeCompare(b.riskLevel ?? '');
});
violations = uniqBy(violations, 'id');
if (!violations?.length) {
return null;
}
return (
Findings
{violations.slice(0, 4).map((violation, index) => (
{violation.name}
))}
{violations.length > 4 && (
+ {violations.length - 4} additional finding
{violations.length - 4 > 1 ? 's' : ''}
)}
>
}
props={{
tooltipTrigger: { className: 'mx-auto pr-0' },
tooltipContent: {
align: 'center',
side: 'top',
className: 'bg-background text-primary',
},
}}
>
v.riskLevel === 'critical'),
'bg-slate-500/20 text-slate-500': !violations.some(
v => v.riskLevel === 'critical',
),
},
)}
>
{violations.length}
);
},
header: 'Findings',
}),
columnHelper.accessor('reportType', {
cell: info => {
const scanType = REPORT_TYPE_TO_SCAN_TYPE[info.getValue()];
return {scanType};
},
header: 'Scan Type',
}),
columnHelper.accessor('monitoringStatus', {
cell: ({ getValue }) => {
const value = getValue();
return (
This merchant is {!value && 'not '}subscribed to recurring ongoing monitoring
}
props={{
tooltipTrigger: { className: 'flex w-full justify-start' },
tooltipContent: { align: 'center', side: 'top' },
}}
>
{value ? (
) : (
)}
);
},
header: () => (
Indicates whether the merchant is subscribed to ongoing monitoring}
props={{
tooltipTrigger: { className: 'mx-auto' },
tooltipContent: { align: 'center', side: 'top' },
}}
>
Monitored
),
}),
columnHelper.accessor('isAlert', {
cell: ({ getValue }) => {
return getValue() ? (
) : (
);
},
header: () => Alert
,
meta: {
conditional: true,
showColumn: !isDemoAccount,
},
}),
columnHelper.accessor('displayDate', {
cell: info => {
const displayDate = info.getValue();
// Convert UTC time to local browser time
const localDateTime = dayjs.utc(displayDate).local();
const date = localDateTime.format('MMM DD, YYYY');
const time = localDateTime.format('HH:mm');
return (
{date}
{time}
);
},
header: 'Created At',
}),
columnHelper.accessor('id', {
cell: info => {
// eslint-disable-next-line react-hooks/rules-of-hooks -- ESLint doesn't like `cell` not being `Cell`.
const { ref, styles } = useEllipsesWithTitle();
const id = info.getValue();
return (
{id}
);
},
header: 'Report ID',
}),
columnHelper.accessor('business.correlationId', {
cell: info => {
// eslint-disable-next-line react-hooks/rules-of-hooks -- ESLint doesn't like `cell` not being `Cell`.
const { ref, styles } = useEllipsesWithTitle();
const merchantId = info.getValue() ?? info.row.original.business?.id;
return (
{merchantId}
);
},
header: 'Merchant ID',
}),
columnHelper.accessor('status', {
meta: {
useWrapper: true,
},
cell: info => {
const status = info.getValue() as keyof typeof statusToData;
return (
);
},
header: 'Status',
}),
];
return columns.filter(column => {
const meta = column.meta;
if (meta?.conditional) {
return meta.showColumn;
}
return true;
});
}, [isDemoAccount]);
};