diff --git a/packages/ui/src/components/templates/report/adapters/report-adapter/report-adapter.ts b/packages/ui/src/components/templates/report/adapters/report-adapter/report-adapter.ts new file mode 100644 index 0000000000..4adf5a9835 --- /dev/null +++ b/packages/ui/src/components/templates/report/adapters/report-adapter/report-adapter.ts @@ -0,0 +1,218 @@ +import { adsProviderAdapter } from '@/components'; +import { AdsProviders, severityToDisplaySeverity } from '@/components/templates/report/constants'; +import { TAdsProvider } from '@/components/templates/report/types'; +import { SeverityType } from '@ballerine/common'; +import dayjs from 'dayjs'; +import { capitalize } from 'string-ts'; + +const getLabel = ({ label, provider }: { label: string; provider: string }) => { + if (label === 'page') { + return `${provider} Page`; + } + + return label; +}; + +export const toRiskLabels = (riskIndicators: Array<{ name: string; riskLevel: string }>) => { + if (!Array.isArray(riskIndicators) || !riskIndicators.length) { + return []; + } + + return riskIndicators.map(({ name, riskLevel, ...rest }) => ({ + label: name, + severity: + severityToDisplaySeverity[riskLevel as keyof typeof severityToDisplaySeverity] ?? riskLevel, + })); +}; + +const normalizeRiskLevel = (riskTypeLevels: Record) => { + return Object.entries(riskTypeLevels).reduce((acc, [riskType, riskLevel]) => { + acc[riskType] = + severityToDisplaySeverity[riskLevel as keyof typeof severityToDisplaySeverity] ?? riskLevel; + + return acc; + }, {} as Record); +}; + +const normalizeHyphenedDataString = (str: string) => { + const parts = str.split(' - '); + + return { + label: capitalize(parts.length > 1 ? parts.slice(0, -1).join(' - ') : parts.at(0) ?? 'Unknown'), + value: parts.at(-1), + }; +}; + +export const reportAdapter = { + DEFAULT: (report: Record) => { + return { + websitesCompanyAnalysis: toRiskLabels( + report?.summary?.riskIndicatorsByDomain?.companyNameViolations, + ), + adsAndSocialMediaAnalysis: toRiskLabels( + report?.summary?.riskIndicatorsByDomain?.adsAndSocialViolations, + ), + adsAndSocialMediaPresence: [ + ...Object.entries({ facebook: report?.socialMedia?.facebookData ?? {} }), + ...Object.entries({ instagram: report?.socialMedia?.instagramData ?? {} }), + ] + .map(([provider, data]) => { + if (!AdsProviders.includes(provider.toUpperCase() as TAdsProvider)) { + return; + } + + const adapter = adsProviderAdapter[provider as keyof typeof adsProviderAdapter]; + const adaptedData = adapter(data); + + return { + label: provider, + items: Object.entries(adaptedData).map(([label, value]) => ({ + label: getLabel({ + label, + provider, + }), + value, + })), + }; + }) + ?.filter((value): value is NonNullable => Boolean(value)), + websiteLineOfBusinessAnalysis: + report?.summary?.riskIndicatorsByDomain?.lineOfBusinessViolations?.map( + ({ + name, + riskLevel, + sourceUrl, + screenshot, + explanation, + }: { + name: string; + riskLevel: string; + sourceUrl: string; + screenshot: { + screenshotUrl: string; + }; + explanation: string; + }) => ({ + label: name, + severity: + severityToDisplaySeverity[riskLevel as keyof typeof severityToDisplaySeverity] ?? + riskLevel, + screenshotUrl: screenshot?.screenshotUrl, + sourceUrl, + explanation, + }), + ), + ecosystemAnalysis: toRiskLabels(report?.summary?.riskIndicatorsByDomain?.ecosystemViolations), + ecosystemMatches: report?.ecosystem?.domains?.map( + ({ + domain, + relatedNode, + relatedNodeType, + indicator, + }: { + domain: string; + relatedNode: string; + relatedNodeType: string; + indicator: Record< + string, + { + name: string; + riskLevel: string; + } + >; + }) => ({ + matchedName: domain, + relatedNode, + relatedNodeType: relatedNodeType, + indicators: { + label: indicator?.name, + severity: + severityToDisplaySeverity[ + indicator?.riskLevel as unknown as keyof typeof severityToDisplaySeverity + ] ?? indicator?.riskLevel, + }, + }), + ), + websiteCredibilityAnalysis: toRiskLabels( + report?.summary?.riskIndicatorsByDomain?.tldViolations, + ), + adsImages: [ + ...Object.entries({ facebook: report?.socialMedia?.facebookData ?? {} }), + ...Object.entries({ instagram: report?.socialMedia?.instagramData ?? {} }), + ] + .map(([provider, data]) => ({ + provider, + src: data?.screenshotUrl, + link: data?.pageUrl, + })) + .filter(({ src }: { src: string }) => !!src), + relatedAdsImages: report?.socialMedia?.pickedAds + ?.map((data: { screenshotUrl: string; link: string }) => ({ + src: data?.screenshotUrl, + link: data?.link, + })) + .filter(({ src }: { src: string }) => !!src), + onlineReputationAnalysis: report?.transactionLaundering?.scamOrFraud?.indicators + ?.filter(({ violation }: { violation: string }) => !!violation) + ?.map(({ violation, sourceUrl }: { violation: string; sourceUrl: string }) => ({ + label: violation, + url: sourceUrl, + })), + summary: report?.summary?.summary, + ongoingMonitoringSummary: report?.summary?.ongoingMonitoringSummary, + riskScore: report?.summary?.riskScore, + riskLevels: normalizeRiskLevel(report?.summary?.riskLevels ?? {}), + companyReputationAnalysis: report?.websiteCompanyAnalysis?.scamOrFraud?.indicators + ?.filter(({ violation }: { violation: string }) => !!violation) + ?.map(({ violation, sourceUrl }: { violation: string; sourceUrl: string }) => ({ + label: violation, + url: sourceUrl, + })), + lineOfBusinessDescription: report?.lineOfBusiness?.lobDescription, + relatedAdsSummary: report?.socialMedia?.relatedAds?.summary, + pricingAnalysis: report?.transactionLaundering?.pricingAnalysis?.indicators, + websiteStructureAndContentEvaluation: + report?.transactionLaundering?.websiteStructureEvaluation?.indicators, + // Example data: + // { + // "trafficSources": [ + // "search / organic - 46.31%", + // "direct - 42.68%" + // ], + // "engagements": [ + // "Time on site - 117.09 seconds", + // "Page per visit - 2.71", + // "Bounce rate - 44.76%" + // ], + // "montlyVisitsIndicators": [ + // "May 2024 - 1987263", + // "June 2024 - 3500503", + // "July 2024 - 1671071", + // "August 2024 - 1793033", + // "September 2024 - 2520118", + // "October 2024 - 3384101" + // ] + // } + trafficAnalysis: { + montlyVisitsIndicators: ( + report?.transactionLaundering?.trafficAnalysis?.montlyVisitsIndicators ?? [] + ).map((item: string) => { + const normalized = normalizeHyphenedDataString(item); + + return { + label: dayjs(normalized.label, 'MMMM YYYY').format('MMM YYYY'), + value: normalized.value, + }; + }), + trafficSources: (report?.transactionLaundering?.trafficAnalysis?.trafficSources ?? []).map( + normalizeHyphenedDataString, + ), + engagements: (report?.transactionLaundering?.trafficAnalysis?.engagements ?? []).map( + normalizeHyphenedDataString, + ), + }, + homepageScreenshotUrl: report?.homepageScreenshot, + formattedMcc: report?.lineOfBusiness?.formattedMcc, + }; + }, +} as const; diff --git a/packages/ui/src/components/templates/report/components/WebsiteCredibility/WebsiteCredibility.tsx b/packages/ui/src/components/templates/report/components/WebsiteCredibility/WebsiteCredibility.tsx index afdd4ea840..d52ad59495 100644 --- a/packages/ui/src/components/templates/report/components/WebsiteCredibility/WebsiteCredibility.tsx +++ b/packages/ui/src/components/templates/report/components/WebsiteCredibility/WebsiteCredibility.tsx @@ -1,10 +1,12 @@ +import { ctw } from '@/common'; +import { Card, CardContent, CardHeader } from '@/components'; +import { BallerineLink } from '@/components/atoms/BallerineLink/BallerineLink'; import { RiskIndicators } from '@/components/molecules/RiskIndicators/RiskIndicators'; +import { FunctionComponent } from 'react'; import { Bar, BarChart, CartesianGrid, - Cell, - Legend, Pie, PieChart, ResponsiveContainer, @@ -12,10 +14,6 @@ import { XAxis, YAxis, } from 'recharts'; -import React, { FunctionComponent } from 'react'; -import { Card, CardContent, CardHeader } from '@/components'; -import { ctw } from '@/common'; -import { BallerineLink } from '@/components/atoms/BallerineLink/BallerineLink'; export const WebsiteCredibility: FunctionComponent<{ violations: Array<{ @@ -119,7 +117,7 @@ export const WebsiteCredibility: FunctionComponent<{ barSize={46} > - + {/* } /> */} @@ -131,7 +129,7 @@ export const WebsiteCredibility: FunctionComponent<{
- Hello world - + */}