amliq-dashboard 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (492) hide show
  1. package/.env.example +3 -0
  2. package/.env.production +1 -0
  3. package/Dockerfile +26 -0
  4. package/QUICK_START.md +267 -0
  5. package/README.md +161 -0
  6. package/TESTING_GUIDE.md +322 -0
  7. package/dist/.well-known/ai-plugin.json +15 -0
  8. package/dist/_headers +8 -0
  9. package/dist/_redirects +1 -0
  10. package/dist/assets/APIKeys-BvwFauIs.js +6 -0
  11. package/dist/assets/APIKeys-DaMSBZQL.js +1 -0
  12. package/dist/assets/AdverseMedia-DckXfMzS.js +4 -0
  13. package/dist/assets/AlertDetailPage-Bc2_zwu_.js +1 -0
  14. package/dist/assets/AlertQueue-DFtBiRoM.js +8 -0
  15. package/dist/assets/Analytics-DtruB5lQ.js +1 -0
  16. package/dist/assets/AuditTrail-BK68ePu0.js +1 -0
  17. package/dist/assets/AuthDivider-gRHEt7gx.js +1 -0
  18. package/dist/assets/Badge-B9VFkofy.js +1 -0
  19. package/dist/assets/BatchJobs-Bz6KFnXD.js +6 -0
  20. package/dist/assets/BillingPage-R9nQ5nFb.js +11 -0
  21. package/dist/assets/Card-Cw-T_-oU.js +1 -0
  22. package/dist/assets/CaseDetail-DYs7Zg_y.js +7 -0
  23. package/dist/assets/CaseManagement-B1Q4Dp1Y.js +1 -0
  24. package/dist/assets/ComplianceReport-Dhssqc9T.js +1 -0
  25. package/dist/assets/Configuration-B92kl9T0.js +1 -0
  26. package/dist/assets/CryptoScreening-C7yFlskC.js +1 -0
  27. package/dist/assets/Dashboard-w4-Nhv9q.js +17 -0
  28. package/dist/assets/DataSources-u3-Ri_5_.js +3 -0
  29. package/dist/assets/EDDWorkflow-CFzlEO0e.js +1 -0
  30. package/dist/assets/EmptyState-Do0xJAT9.js +6 -0
  31. package/dist/assets/ForgotPassword-BPYyQFQp.js +1 -0
  32. package/dist/assets/LandingPage-CjaP0zw4.js +41 -0
  33. package/dist/assets/ListsMarketplace-CG6KkT9B.js +6 -0
  34. package/dist/assets/Login-677LEGP1.js +1 -0
  35. package/dist/assets/MFASetup-BxqCPvui.js +1 -0
  36. package/dist/assets/Monitoring-9TN9MK4y.js +1 -0
  37. package/dist/assets/Onboarding-Cf0Y83lX.js +1 -0
  38. package/dist/assets/Operations-CcXwc2xw.js +10 -0
  39. package/dist/assets/Overview-B_Ky0VWV.js +1 -0
  40. package/dist/assets/PEPScreening-pUQjTH9c.js +1 -0
  41. package/dist/assets/PageHeader-BObHFn0i.js +1 -0
  42. package/dist/assets/PrivacyPolicy-DZ2xrKir.js +1 -0
  43. package/dist/assets/ResetPassword-AtxtpIG1.js +1 -0
  44. package/dist/assets/RiskAssessment-Dzuh1Usv.js +1 -0
  45. package/dist/assets/SARForm-D8w2ZGe-.js +1 -0
  46. package/dist/assets/SanctionsLists-CPA3UH2v.js +1 -0
  47. package/dist/assets/ScheduledTasks-Dsjk-6UR.js +1 -0
  48. package/dist/assets/ScreenEntity-D1U0YL7v.js +3 -0
  49. package/dist/assets/ScreeningProgress-BW49PzoB.js +66 -0
  50. package/dist/assets/SearchField-CulFKdiI.js +1 -0
  51. package/dist/assets/Signup-C0FmFOo_.js +1 -0
  52. package/dist/assets/SystemHealth-B_8u4eva.js +1 -0
  53. package/dist/assets/TaskHistory-BCmEM89q.js +3 -0
  54. package/dist/assets/Team-CUo_FlU7.js +1 -0
  55. package/dist/assets/TenantDetail-CgZQHUY8.js +1 -0
  56. package/dist/assets/Tenants-RB9E9ick.js +2 -0
  57. package/dist/assets/TermsOfService-BfL-kndX.js +1 -0
  58. package/dist/assets/TransactionMonitoring-DGh_T22s.js +14 -0
  59. package/dist/assets/TxnScreening-B7cm2-eh.js +1 -0
  60. package/dist/assets/UBOChain-falXAsCo.js +1 -0
  61. package/dist/assets/Users-Doakpg9c.js +1 -0
  62. package/dist/assets/Webhooks-BZpAfaxv.js +1 -0
  63. package/dist/assets/alert-triangle-YCFVm7B_.js +6 -0
  64. package/dist/assets/alerts-COUnTwWY.js +1 -0
  65. package/dist/assets/arrow-left-9ApLqP95.js +6 -0
  66. package/dist/assets/check-VTcvVtIU.js +6 -0
  67. package/dist/assets/check-circle-2-Dn-lG5YQ.js +6 -0
  68. package/dist/assets/code-DIJRWFVv.js +6 -0
  69. package/dist/assets/fingerprint-C0MGVtax.js +6 -0
  70. package/dist/assets/flag-B8_Gwxh6.js +6 -0
  71. package/dist/assets/index-CfdYcqRM.js +289 -0
  72. package/dist/assets/index-HPU_Rhdu.css +1 -0
  73. package/dist/assets/layers-zHz2o4vo.js +6 -0
  74. package/dist/assets/loader-B9_dNihg.js +6 -0
  75. package/dist/assets/mail-BijL2qwp.js +6 -0
  76. package/dist/assets/plus-i0oBIIPS.js +6 -0
  77. package/dist/assets/refresh-cw-CSuAwutt.js +6 -0
  78. package/dist/assets/useAnalytics-Bj8IONcw.js +72 -0
  79. package/dist/assets/x-circle-DLGKvijE.js +6 -0
  80. package/dist/favicon.svg +15 -0
  81. package/dist/index.html +51 -0
  82. package/dist/llms.txt +28 -0
  83. package/dist/logo.png +0 -0
  84. package/dist/logo.svg +17 -0
  85. package/dist/manifest.json +44 -0
  86. package/dist/robots.txt +15 -0
  87. package/dist/schema.json +13 -0
  88. package/dist/sitemap.xml +28 -0
  89. package/dist/sw.js +67 -0
  90. package/e2e/auth-setup.ts +20 -0
  91. package/e2e/billing.spec.ts +50 -0
  92. package/e2e/cases.spec.ts +53 -0
  93. package/e2e/compliance.spec.ts +65 -0
  94. package/e2e/config.spec.ts +56 -0
  95. package/e2e/dashboard.spec.ts +47 -0
  96. package/e2e/fixtures.ts +33 -0
  97. package/e2e/lists.spec.ts +43 -0
  98. package/e2e/login.spec.ts +64 -0
  99. package/e2e/media.spec.ts +48 -0
  100. package/e2e/mocks.ts +51 -0
  101. package/e2e/monitoring.spec.ts +44 -0
  102. package/e2e/navigation.spec.ts +56 -0
  103. package/e2e/onboarding.spec.ts +49 -0
  104. package/e2e/responsive.spec.ts +40 -0
  105. package/e2e/risk.spec.ts +61 -0
  106. package/e2e/screening.spec.ts +76 -0
  107. package/e2e/team.spec.ts +53 -0
  108. package/index.html +50 -0
  109. package/package.json +47 -0
  110. package/playwright.config.ts +35 -0
  111. package/postcss.config.js +6 -0
  112. package/public/.well-known/ai-plugin.json +15 -0
  113. package/public/_headers +8 -0
  114. package/public/_redirects +1 -0
  115. package/public/favicon.svg +15 -0
  116. package/public/llms.txt +28 -0
  117. package/public/logo.png +0 -0
  118. package/public/logo.svg +17 -0
  119. package/public/manifest.json +44 -0
  120. package/public/robots.txt +15 -0
  121. package/public/schema.json +13 -0
  122. package/public/sitemap.xml +28 -0
  123. package/public/sw.js +67 -0
  124. package/scripts/test-runner.sh +152 -0
  125. package/src/App.tsx +48 -0
  126. package/src/api/alerts.ts +19 -0
  127. package/src/api/analytics.ts +6 -0
  128. package/src/api/audit.ts +11 -0
  129. package/src/api/auth.ts +26 -0
  130. package/src/api/billing.ts +54 -0
  131. package/src/api/cases.ts +48 -0
  132. package/src/api/client.ts +50 -0
  133. package/src/api/config.ts +30 -0
  134. package/src/api/edd.ts +25 -0
  135. package/src/api/enforcement.ts +33 -0
  136. package/src/api/lists.ts +24 -0
  137. package/src/api/monitoring.ts +82 -0
  138. package/src/api/pep.ts +30 -0
  139. package/src/api/risk.ts +26 -0
  140. package/src/api/screening.ts +37 -0
  141. package/src/api/team.ts +27 -0
  142. package/src/api/transactions.ts +37 -0
  143. package/src/components/admin/TenantCards.tsx +51 -0
  144. package/src/components/alerts/AlertActions.test.tsx +80 -0
  145. package/src/components/alerts/AlertActions.tsx +68 -0
  146. package/src/components/alerts/AlertCard.test.tsx +86 -0
  147. package/src/components/alerts/AlertCard.tsx +60 -0
  148. package/src/components/alerts/AlertDetailSidebar.tsx +63 -0
  149. package/src/components/alerts/AlertFilters.test.tsx +73 -0
  150. package/src/components/alerts/AlertFilters.tsx +88 -0
  151. package/src/components/alerts/EntityDetailsCard.test.tsx +61 -0
  152. package/src/components/alerts/EntityDetailsCard.tsx +37 -0
  153. package/src/components/alerts/NotesCard.test.tsx +39 -0
  154. package/src/components/alerts/NotesCard.tsx +28 -0
  155. package/src/components/auth/AuthDivider.tsx +18 -0
  156. package/src/components/auth/LoginForm.tsx +62 -0
  157. package/src/components/auth/LoginLeftPanel.tsx +43 -0
  158. package/src/components/auth/PasswordStrength.tsx +26 -0
  159. package/src/components/auth/SignInButtons.tsx +46 -0
  160. package/src/components/auth/SignupLeftPanel.tsx +35 -0
  161. package/src/components/auth/countries.ts +17 -0
  162. package/src/components/charts/AreaChart.tsx +45 -0
  163. package/src/components/charts/BarChart.tsx +48 -0
  164. package/src/components/charts/DonutChart.tsx +47 -0
  165. package/src/components/compliance/CaseActions.tsx +74 -0
  166. package/src/components/compliance/CaseTimeline.tsx +68 -0
  167. package/src/components/compliance/MediaResultCard.tsx +87 -0
  168. package/src/components/compliance/PEPResultCard.tsx +78 -0
  169. package/src/components/config/MatchingModesCard.test.tsx +75 -0
  170. package/src/components/config/MatchingModesCard.tsx +40 -0
  171. package/src/components/config/ScreeningLayersCard.test.tsx +57 -0
  172. package/src/components/config/ScreeningLayersCard.tsx +36 -0
  173. package/src/components/config/ThresholdsCard.test.tsx +79 -0
  174. package/src/components/config/ThresholdsCard.tsx +51 -0
  175. package/src/components/dashboard/ActivityFeed.tsx +93 -0
  176. package/src/components/dashboard/DashboardGreeting.tsx +23 -0
  177. package/src/components/dashboard/DashboardSkeleton.tsx +35 -0
  178. package/src/components/dashboard/QuickActions.tsx +52 -0
  179. package/src/components/dashboard/TopEntitiesTable.tsx +63 -0
  180. package/src/components/data/ComplianceMetrics.test.tsx +32 -0
  181. package/src/components/data/ComplianceMetrics.tsx +40 -0
  182. package/src/components/data/ConfidenceScore.test.tsx +62 -0
  183. package/src/components/data/ConfidenceScore.tsx +27 -0
  184. package/src/components/data/StatCard.test.tsx +72 -0
  185. package/src/components/data/StatCard.tsx +63 -0
  186. package/src/components/data/StatusBadge.test.tsx +63 -0
  187. package/src/components/data/StatusBadge.tsx +39 -0
  188. package/src/components/layout/AppShell.tsx +48 -0
  189. package/src/components/layout/Breadcrumbs.tsx +48 -0
  190. package/src/components/layout/CommandPalette.tsx +81 -0
  191. package/src/components/layout/DashboardLayout.tsx +19 -0
  192. package/src/components/layout/MobileHeader.tsx +30 -0
  193. package/src/components/layout/NavGroup.test.tsx +63 -0
  194. package/src/components/layout/NavGroup.tsx +64 -0
  195. package/src/components/layout/NotificationBell.tsx +89 -0
  196. package/src/components/layout/PageHeader.test.tsx +61 -0
  197. package/src/components/layout/PageHeader.tsx +19 -0
  198. package/src/components/layout/ProtectedRoute.tsx +31 -0
  199. package/src/components/layout/PublicLayout.tsx +17 -0
  200. package/src/components/layout/Sidebar.test.tsx +67 -0
  201. package/src/components/layout/Sidebar.tsx +92 -0
  202. package/src/components/layout/Toolbar.test.tsx +47 -0
  203. package/src/components/layout/Toolbar.tsx +68 -0
  204. package/src/components/layout/navItems.ts +94 -0
  205. package/src/components/lists/ListCard.tsx +78 -0
  206. package/src/components/lists/ListMarketplaceCard.tsx +77 -0
  207. package/src/components/screening/CircularConfidence.tsx +38 -0
  208. package/src/components/screening/LimitReachedBanner.tsx +31 -0
  209. package/src/components/screening/ListSelector.tsx +64 -0
  210. package/src/components/screening/MatchDetailHeader.tsx +64 -0
  211. package/src/components/screening/MatchEntityInfo.tsx +56 -0
  212. package/src/components/screening/MatchEvidenceBars.tsx +65 -0
  213. package/src/components/screening/MatchMetadata.tsx +95 -0
  214. package/src/components/screening/ScreenResults.tsx +49 -0
  215. package/src/components/screening/ScreeningForm.test.tsx +83 -0
  216. package/src/components/screening/ScreeningForm.tsx +100 -0
  217. package/src/components/screening/ScreeningLayersList.test.tsx +33 -0
  218. package/src/components/screening/ScreeningLayersList.tsx +46 -0
  219. package/src/components/screening/ScreeningProgress.tsx +91 -0
  220. package/src/components/screening/ScreeningQuotaBanner.tsx +64 -0
  221. package/src/components/screening/ScreeningResultCard.tsx +47 -0
  222. package/src/components/screening/ScreeningResultRow.tsx +45 -0
  223. package/src/components/screening/ScreeningResults.test.tsx +37 -0
  224. package/src/components/screening/ScreeningResults.tsx +33 -0
  225. package/src/components/screening/ShareResults.tsx +103 -0
  226. package/src/components/screening/ThresholdSlider.tsx +23 -0
  227. package/src/components/transactions/WebhookCTA.tsx +63 -0
  228. package/src/components/ui/Avatar.test.tsx +47 -0
  229. package/src/components/ui/Avatar.tsx +35 -0
  230. package/src/components/ui/Badge.test.tsx +49 -0
  231. package/src/components/ui/Badge.tsx +33 -0
  232. package/src/components/ui/Button.test.tsx +56 -0
  233. package/src/components/ui/Button.tsx +46 -0
  234. package/src/components/ui/Card.test.tsx +61 -0
  235. package/src/components/ui/Card.tsx +29 -0
  236. package/src/components/ui/ConfirmModal.tsx +67 -0
  237. package/src/components/ui/Divider.test.tsx +24 -0
  238. package/src/components/ui/Divider.tsx +5 -0
  239. package/src/components/ui/EmptyState.test.tsx +49 -0
  240. package/src/components/ui/EmptyState.tsx +22 -0
  241. package/src/components/ui/ErrorBoundary.tsx +44 -0
  242. package/src/components/ui/ExportMenu.tsx +71 -0
  243. package/src/components/ui/LanguageSwitcher.tsx +37 -0
  244. package/src/components/ui/LoadingSpinner.test.tsx +41 -0
  245. package/src/components/ui/LoadingSpinner.tsx +21 -0
  246. package/src/components/ui/MetricCard.tsx +63 -0
  247. package/src/components/ui/ScoreRing.tsx +51 -0
  248. package/src/components/ui/SearchField.test.tsx +55 -0
  249. package/src/components/ui/SearchField.tsx +31 -0
  250. package/src/components/ui/SeverityBadge.tsx +57 -0
  251. package/src/components/ui/ThemeToggle.tsx +37 -0
  252. package/src/components/ui/Toast.test.tsx +79 -0
  253. package/src/components/ui/Toast.tsx +75 -0
  254. package/src/components/ui/Toggle.test.tsx +85 -0
  255. package/src/components/ui/Toggle.tsx +46 -0
  256. package/src/context/AuthContext.tsx +71 -0
  257. package/src/data/pepProfiles.ts +76 -0
  258. package/src/data/pepProfilesExtra.ts +58 -0
  259. package/src/hooks/useAlerts.ts +36 -0
  260. package/src/hooks/useAnalytics.ts +23 -0
  261. package/src/hooks/useApi.test.ts +79 -0
  262. package/src/hooks/useApi.ts +33 -0
  263. package/src/hooks/useAudit.ts +28 -0
  264. package/src/hooks/useBilling.ts +38 -0
  265. package/src/hooks/useConfig.ts +35 -0
  266. package/src/hooks/useDebounce.test.ts +84 -0
  267. package/src/hooks/useDebounce.ts +15 -0
  268. package/src/hooks/useDirection.ts +15 -0
  269. package/src/hooks/useLists.ts +34 -0
  270. package/src/hooks/useMediaQuery.test.ts +97 -0
  271. package/src/hooks/useMediaQuery.ts +28 -0
  272. package/src/hooks/useScreening.ts +33 -0
  273. package/src/hooks/useSidebar.ts +18 -0
  274. package/src/hooks/useUsage.ts +27 -0
  275. package/src/i18n/config.ts +33 -0
  276. package/src/i18n/locales/ar/admin.json +19 -0
  277. package/src/i18n/locales/ar/alerts.json +52 -0
  278. package/src/i18n/locales/ar/analytics.json +9 -0
  279. package/src/i18n/locales/ar/audit.json +12 -0
  280. package/src/i18n/locales/ar/auth.json +60 -0
  281. package/src/i18n/locales/ar/batch.json +5 -0
  282. package/src/i18n/locales/ar/billing.json +41 -0
  283. package/src/i18n/locales/ar/common.json +65 -0
  284. package/src/i18n/locales/ar/compliance.json +83 -0
  285. package/src/i18n/locales/ar/config.json +13 -0
  286. package/src/i18n/locales/ar/dashboard.json +19 -0
  287. package/src/i18n/locales/ar/errors.json +9 -0
  288. package/src/i18n/locales/ar/index.ts +29 -0
  289. package/src/i18n/locales/ar/legal.json +10 -0
  290. package/src/i18n/locales/ar/lists.json +6 -0
  291. package/src/i18n/locales/ar/marketing.json +110 -0
  292. package/src/i18n/locales/ar/monitoring.json +15 -0
  293. package/src/i18n/locales/ar/nav.json +35 -0
  294. package/src/i18n/locales/ar/onboarding.json +25 -0
  295. package/src/i18n/locales/ar/platform.json +23 -0
  296. package/src/i18n/locales/ar/screening.json +26 -0
  297. package/src/i18n/locales/ar/team.json +11 -0
  298. package/src/i18n/locales/en/admin.json +21 -0
  299. package/src/i18n/locales/en/alerts.json +52 -0
  300. package/src/i18n/locales/en/analytics.json +9 -0
  301. package/src/i18n/locales/en/audit.json +12 -0
  302. package/src/i18n/locales/en/auth.json +60 -0
  303. package/src/i18n/locales/en/batch.json +5 -0
  304. package/src/i18n/locales/en/billing.json +87 -0
  305. package/src/i18n/locales/en/common.json +65 -0
  306. package/src/i18n/locales/en/compliance.json +83 -0
  307. package/src/i18n/locales/en/config.json +29 -0
  308. package/src/i18n/locales/en/dashboard.json +23 -0
  309. package/src/i18n/locales/en/errors.json +9 -0
  310. package/src/i18n/locales/en/index.ts +29 -0
  311. package/src/i18n/locales/en/legal.json +114 -0
  312. package/src/i18n/locales/en/lists.json +6 -0
  313. package/src/i18n/locales/en/marketing.json +174 -0
  314. package/src/i18n/locales/en/monitoring.json +15 -0
  315. package/src/i18n/locales/en/nav.json +35 -0
  316. package/src/i18n/locales/en/onboarding.json +31 -0
  317. package/src/i18n/locales/en/platform.json +25 -0
  318. package/src/i18n/locales/en/screening.json +26 -0
  319. package/src/i18n/locales/en/team.json +12 -0
  320. package/src/i18n/locales/he/admin.json +19 -0
  321. package/src/i18n/locales/he/alerts.json +52 -0
  322. package/src/i18n/locales/he/analytics.json +9 -0
  323. package/src/i18n/locales/he/audit.json +12 -0
  324. package/src/i18n/locales/he/auth.json +60 -0
  325. package/src/i18n/locales/he/batch.json +5 -0
  326. package/src/i18n/locales/he/billing.json +41 -0
  327. package/src/i18n/locales/he/common.json +65 -0
  328. package/src/i18n/locales/he/compliance.json +83 -0
  329. package/src/i18n/locales/he/config.json +13 -0
  330. package/src/i18n/locales/he/dashboard.json +19 -0
  331. package/src/i18n/locales/he/errors.json +9 -0
  332. package/src/i18n/locales/he/index.ts +29 -0
  333. package/src/i18n/locales/he/legal.json +10 -0
  334. package/src/i18n/locales/he/lists.json +6 -0
  335. package/src/i18n/locales/he/marketing.json +110 -0
  336. package/src/i18n/locales/he/monitoring.json +15 -0
  337. package/src/i18n/locales/he/nav.json +35 -0
  338. package/src/i18n/locales/he/onboarding.json +25 -0
  339. package/src/i18n/locales/he/platform.json +23 -0
  340. package/src/i18n/locales/he/screening.json +26 -0
  341. package/src/i18n/locales/he/team.json +11 -0
  342. package/src/index.css +112 -0
  343. package/src/main.tsx +15 -0
  344. package/src/pages/APIKeys.tsx +120 -0
  345. package/src/pages/AddMonitorModal.tsx +30 -0
  346. package/src/pages/AdverseMedia.test.tsx +18 -0
  347. package/src/pages/AdverseMedia.tsx +89 -0
  348. package/src/pages/AlertDetailPage.tsx +64 -0
  349. package/src/pages/AlertQueue.test.tsx +48 -0
  350. package/src/pages/AlertQueue.tsx +63 -0
  351. package/src/pages/Analytics.tsx +50 -0
  352. package/src/pages/AuditTrail.tsx +64 -0
  353. package/src/pages/BatchJobs.tsx +79 -0
  354. package/src/pages/CaseDetail.tsx +72 -0
  355. package/src/pages/CaseManagement.test.tsx +31 -0
  356. package/src/pages/CaseManagement.tsx +92 -0
  357. package/src/pages/Configuration.test.tsx +96 -0
  358. package/src/pages/Configuration.tsx +123 -0
  359. package/src/pages/CryptoScreening.tsx +109 -0
  360. package/src/pages/Dashboard.test.tsx +51 -0
  361. package/src/pages/Dashboard.tsx +66 -0
  362. package/src/pages/EDDWorkflow.test.tsx +29 -0
  363. package/src/pages/EDDWorkflow.tsx +73 -0
  364. package/src/pages/ForgotPassword.test.tsx +49 -0
  365. package/src/pages/ForgotPassword.tsx +67 -0
  366. package/src/pages/ListsMarketplace.tsx +102 -0
  367. package/src/pages/Login.test.tsx +100 -0
  368. package/src/pages/Login.tsx +57 -0
  369. package/src/pages/MFASetup.tsx +114 -0
  370. package/src/pages/MonitorProfileCard.tsx +27 -0
  371. package/src/pages/Monitoring.tsx +68 -0
  372. package/src/pages/Onboarding.test.tsx +36 -0
  373. package/src/pages/Onboarding.tsx +60 -0
  374. package/src/pages/PEPScreening.test.tsx +15 -0
  375. package/src/pages/PEPScreening.tsx +100 -0
  376. package/src/pages/ResetPassword.tsx +81 -0
  377. package/src/pages/RiskAssessment.test.tsx +15 -0
  378. package/src/pages/RiskAssessment.tsx +108 -0
  379. package/src/pages/SanctionsLists.tsx +74 -0
  380. package/src/pages/ScreenEntity.test.tsx +82 -0
  381. package/src/pages/ScreenEntity.tsx +76 -0
  382. package/src/pages/Signup.test.tsx +98 -0
  383. package/src/pages/Signup.tsx +92 -0
  384. package/src/pages/TaskHistory.tsx +183 -0
  385. package/src/pages/Team.test.tsx +15 -0
  386. package/src/pages/Team.tsx +140 -0
  387. package/src/pages/TransactionMonitoring.test.tsx +18 -0
  388. package/src/pages/TransactionMonitoring.tsx +118 -0
  389. package/src/pages/TxnScreening.tsx +125 -0
  390. package/src/pages/UBOChain.test.tsx +35 -0
  391. package/src/pages/UBOChain.tsx +65 -0
  392. package/src/pages/Webhooks.tsx +137 -0
  393. package/src/pages/admin/DataSources.tsx +230 -0
  394. package/src/pages/admin/Operations.tsx +103 -0
  395. package/src/pages/admin/ScheduledTasks.tsx +155 -0
  396. package/src/pages/admin/SystemHealth.tsx +58 -0
  397. package/src/pages/admin/TenantDetail.tsx +62 -0
  398. package/src/pages/admin/Tenants.test.tsx +20 -0
  399. package/src/pages/admin/Tenants.tsx +63 -0
  400. package/src/pages/admin/opsRunners.ts +81 -0
  401. package/src/pages/admin/opsTerminal.tsx +63 -0
  402. package/src/pages/billing/ActiveSubscriptions.tsx +41 -0
  403. package/src/pages/billing/AddProductModal.tsx +99 -0
  404. package/src/pages/billing/BillingPage.test.tsx +30 -0
  405. package/src/pages/billing/BillingPage.tsx +67 -0
  406. package/src/pages/billing/CurrentPlan.tsx +50 -0
  407. package/src/pages/billing/InvoiceList.tsx +104 -0
  408. package/src/pages/billing/InvoiceRow.tsx +37 -0
  409. package/src/pages/billing/LemonSqueezySetup.tsx +51 -0
  410. package/src/pages/billing/PaymentAlert.tsx +33 -0
  411. package/src/pages/billing/PlanComparison.tsx +53 -0
  412. package/src/pages/billing/ProductUsage.tsx +43 -0
  413. package/src/pages/billing/PromoCodeInput.tsx +58 -0
  414. package/src/pages/billing/SeatManager.tsx +80 -0
  415. package/src/pages/billing/SeatRow.tsx +32 -0
  416. package/src/pages/billing/SubscriptionCard.tsx +73 -0
  417. package/src/pages/billing/UpgradeModal.tsx +53 -0
  418. package/src/pages/billing/UsageHistory.tsx +37 -0
  419. package/src/pages/billing/UsageMeter.tsx +26 -0
  420. package/src/pages/billing/UsageOverview.tsx +38 -0
  421. package/src/pages/legal/PrivacyPolicy.tsx +25 -0
  422. package/src/pages/legal/TermsOfService.tsx +25 -0
  423. package/src/pages/marketing/BundleCallout.tsx +24 -0
  424. package/src/pages/marketing/CTASection.tsx +48 -0
  425. package/src/pages/marketing/CaseStudy.tsx +37 -0
  426. package/src/pages/marketing/ComparisonTable.tsx +66 -0
  427. package/src/pages/marketing/CompetitiveEdge.tsx +55 -0
  428. package/src/pages/marketing/DataCoverage.tsx +54 -0
  429. package/src/pages/marketing/DataRain.tsx +30 -0
  430. package/src/pages/marketing/EnterpriseCTA.tsx +26 -0
  431. package/src/pages/marketing/FAQItem.tsx +27 -0
  432. package/src/pages/marketing/FAQSchema.tsx +43 -0
  433. package/src/pages/marketing/FAQSection.tsx +19 -0
  434. package/src/pages/marketing/FeatureDetail.tsx +19 -0
  435. package/src/pages/marketing/FeaturesGrid.tsx +60 -0
  436. package/src/pages/marketing/FeaturesSpotlight.tsx +68 -0
  437. package/src/pages/marketing/FooterSection.tsx +92 -0
  438. package/src/pages/marketing/GradientOrbs.tsx +26 -0
  439. package/src/pages/marketing/HeroSearch.tsx +113 -0
  440. package/src/pages/marketing/HeroSection.tsx +72 -0
  441. package/src/pages/marketing/LandingPage.tsx +45 -0
  442. package/src/pages/marketing/LogoCloud.tsx +31 -0
  443. package/src/pages/marketing/LogoMarquee.tsx +45 -0
  444. package/src/pages/marketing/MarketingNav.tsx +80 -0
  445. package/src/pages/marketing/MatchingLayers.tsx +78 -0
  446. package/src/pages/marketing/MobileMenu.tsx +45 -0
  447. package/src/pages/marketing/PricingCard.tsx +57 -0
  448. package/src/pages/marketing/PricingFeatureRow.tsx +16 -0
  449. package/src/pages/marketing/PricingSection.tsx +103 -0
  450. package/src/pages/marketing/PricingToggle.tsx +40 -0
  451. package/src/pages/marketing/ProductPricingCards.tsx +23 -0
  452. package/src/pages/marketing/ProductShowcase.tsx +81 -0
  453. package/src/pages/marketing/ProductTabs.tsx +45 -0
  454. package/src/pages/marketing/QuoteRotator.tsx +56 -0
  455. package/src/pages/marketing/StatsBar.tsx +81 -0
  456. package/src/pages/marketing/StatsSection.tsx +44 -0
  457. package/src/pages/marketing/TestimonialCard.tsx +38 -0
  458. package/src/pages/marketing/TestimonialsSection.tsx +32 -0
  459. package/src/pages/marketing/animations.tsx +56 -0
  460. package/src/pages/onboarding/CountryStep.tsx +38 -0
  461. package/src/pages/onboarding/ListsStep.tsx +44 -0
  462. package/src/pages/onboarding/StepIndicator.tsx +34 -0
  463. package/src/pages/onboarding/ThresholdStep.tsx +49 -0
  464. package/src/pages/platform/APIKeys.tsx +102 -0
  465. package/src/pages/platform/Overview.tsx +58 -0
  466. package/src/pages/platform/Users.tsx +110 -0
  467. package/src/pages/reporting/ComplianceReport.tsx +99 -0
  468. package/src/pages/reporting/SARForm.tsx +99 -0
  469. package/src/routes/appRoutes.tsx +60 -0
  470. package/src/routes/compliance.tsx +28 -0
  471. package/src/routes/lazyCompliance.ts +34 -0
  472. package/src/routes/lazyPages.ts +35 -0
  473. package/src/routes/lazyPlatform.ts +5 -0
  474. package/src/routes/platform.tsx +15 -0
  475. package/src/styles/effects.css +76 -0
  476. package/src/test/setup.ts +25 -0
  477. package/src/test/utils.tsx +49 -0
  478. package/src/types/alert.ts +31 -0
  479. package/src/types/analytics.ts +16 -0
  480. package/src/types/audit.ts +11 -0
  481. package/src/types/billing.ts +60 -0
  482. package/src/types/common.ts +15 -0
  483. package/src/types/config.ts +19 -0
  484. package/src/types/entity.ts +34 -0
  485. package/src/types/index.ts +8 -0
  486. package/src/types/list.ts +15 -0
  487. package/src/types/screening.ts +32 -0
  488. package/src/vite-env.d.ts +1 -0
  489. package/tailwind.config.js +65 -0
  490. package/tsconfig.json +22 -0
  491. package/vite.config.ts +11 -0
  492. package/vitest.config.ts +19 -0
@@ -0,0 +1,80 @@
1
+ import React, { useState, useEffect } from 'react'
2
+ import { useTranslation } from 'react-i18next'
3
+ import { getSeats, addSeat, removeSeat } from '../../api/billing'
4
+ import { SeatRow } from './SeatRow'
5
+ import { Seat } from '../../types/billing'
6
+ import { Plus } from 'lucide-react'
7
+
8
+ export function SeatManager() {
9
+ const { t } = useTranslation('billing')
10
+ const [seats, setSeats] = useState<Seat[]>([])
11
+ const [email, setEmail] = useState('')
12
+ const [role, setRole] = useState('Investigator')
13
+ const [loading, setLoading] = useState(true)
14
+
15
+ useEffect(() => {
16
+ getSeats()
17
+ .then(s => setSeats(s))
18
+ .catch(() => setSeats([]))
19
+ .finally(() => setLoading(false))
20
+ }, [])
21
+
22
+ const handleAdd = async () => {
23
+ if (!email) return
24
+ const newSeat = await addSeat(email, role)
25
+ setSeats([...seats, newSeat])
26
+ setEmail('')
27
+ setRole('Investigator')
28
+ }
29
+
30
+ const handleRemove = async (userId: string) => {
31
+ await removeSeat(userId)
32
+ setSeats(seats.filter(s => s.userId !== userId))
33
+ }
34
+
35
+ if (loading) return <div className="sf-body text-apple-label-secondary">{t('seats.loading')}</div>
36
+
37
+ return (
38
+ <div className="space-y-md">
39
+ <div className="flex flex-col sm:flex-row gap-xs">
40
+ <input
41
+ type="email"
42
+ value={email}
43
+ onChange={e => setEmail(e.target.value)}
44
+ placeholder={t('seats.email_placeholder')}
45
+ aria-label={t('seats.email_placeholder')}
46
+ className="flex-1 px-md py-sm bg-white/5 border border-white/10 rounded-apple-md sf-body text-white placeholder:text-apple-label-secondary focus:outline-none focus:ring-2 focus:ring-apple-blue focus:border-apple-blue"
47
+ />
48
+ <div className="flex gap-xs">
49
+ <select
50
+ value={role}
51
+ onChange={e => setRole(e.target.value)}
52
+ aria-label={t('seats.role')}
53
+ className="flex-1 sm:flex-none px-md py-sm bg-white/5 border border-white/10 rounded-apple-md sf-body text-white focus:outline-none focus:ring-2 focus:ring-apple-blue focus:border-apple-blue"
54
+ >
55
+ <option>{t('seats.investigator')}</option>
56
+ <option>{t('seats.analyst')}</option>
57
+ <option>{t('seats.manager')}</option>
58
+ </select>
59
+ <button
60
+ onClick={handleAdd}
61
+ aria-label={t('seats.add')}
62
+ className="px-md py-sm bg-apple-blue hover:bg-apple-blue/80 text-white rounded-apple-md flex items-center gap-xs transition-colors cursor-pointer"
63
+ >
64
+ <Plus className="w-4 h-4" />
65
+ </button>
66
+ </div>
67
+ </div>
68
+
69
+ <div className="space-y-xs">
70
+ {seats.map(seat => (
71
+ <SeatRow key={seat.userId} seat={seat} onRemove={handleRemove} />
72
+ ))}
73
+ </div>
74
+
75
+ <p className="sf-caption text-apple-label-secondary">
76
+ {t('seats.used', { current: seats.length, total: 3 })}
77
+ </p>
78
+ </div>
79
+ )
80
+ }
@@ -0,0 +1,32 @@
1
+ import React from 'react'
2
+ import { Seat } from '../../types/billing'
3
+ import { X } from 'lucide-react'
4
+
5
+ interface SeatRowProps {
6
+ seat: Seat
7
+ onRemove: (userId: string) => void
8
+ }
9
+
10
+ export function SeatRow({ seat, onRemove }: SeatRowProps) {
11
+ const initials = (seat.email ?? '').split('@')[0].slice(0, 2).toUpperCase() || '??'
12
+
13
+ return (
14
+ <div className="flex items-center justify-between p-md bg-white/5 rounded-apple-md hover:bg-white/10 transition-colors">
15
+ <div className="flex items-center gap-md">
16
+ <div className="w-8 h-8 rounded-apple-md bg-apple-blue/20 flex items-center justify-center">
17
+ <span className="text-xs font-medium text-apple-blue">{initials}</span>
18
+ </div>
19
+ <div>
20
+ <p className="sf-body text-white">{seat.email}</p>
21
+ <p className="sf-caption text-apple-label-secondary">{seat.role}</p>
22
+ </div>
23
+ </div>
24
+ <button
25
+ onClick={() => onRemove(seat.userId)}
26
+ className="p-xs hover:bg-red-600/20 rounded-apple-md text-red-400 transition-colors"
27
+ >
28
+ <X className="w-4 h-4" />
29
+ </button>
30
+ </div>
31
+ )
32
+ }
@@ -0,0 +1,73 @@
1
+ import React from 'react'
2
+ import { useTranslation } from 'react-i18next'
3
+ import { Subscription } from '../../types/billing'
4
+ import { Zap, LayoutDashboard, Code, Code2, FileText } from 'lucide-react'
5
+ import clsx from 'clsx'
6
+
7
+ interface SubscriptionCardProps {
8
+ subscription: Subscription
9
+ }
10
+
11
+ const productIcons = {
12
+ api: Zap,
13
+ dashboard: LayoutDashboard,
14
+ sdk: Code,
15
+ iframe: Code2,
16
+ dataset: FileText
17
+ }
18
+
19
+ const productNames = {
20
+ api: 'API Access',
21
+ dashboard: 'Dashboard',
22
+ sdk: 'SDK',
23
+ iframe: 'iFrame Widget',
24
+ dataset: 'Dataset CSV'
25
+ }
26
+
27
+ export function SubscriptionCard({ subscription }: SubscriptionCardProps) {
28
+ const { t } = useTranslation('billing')
29
+ const Icon = productIcons[subscription.product as keyof typeof productIcons] ?? Zap
30
+ const name = productNames[subscription.product as keyof typeof productNames] ?? subscription.product
31
+ const price = subscription.plan?.monthlyPrice ?? 0
32
+ const renewDate = new Date(subscription.currentPeriodEnd)
33
+
34
+ return (
35
+ <div className="rounded-apple-lg border border-white/10 bg-white/5 p-lg hover:border-white/20 transition-colors">
36
+ <div className="flex items-start justify-between mb-md">
37
+ <div className="flex items-center gap-md">
38
+ <div className="p-md bg-apple-blue/20 rounded-apple-md">
39
+ <Icon className="w-5 h-5 text-apple-blue" />
40
+ </div>
41
+ <div>
42
+ <h3 className="sf-body font-medium text-white">{name}</h3>
43
+ <p className="sf-caption text-apple-label-secondary">{subscription.plan?.name ?? 'Free'} plan</p>
44
+ </div>
45
+ </div>
46
+ <span className={clsx('text-xs font-medium px-md py-xs rounded-apple-md', subscription.status === 'active' ? 'bg-green-600/20 text-green-400' : 'bg-yellow-600/20 text-yellow-400')}>
47
+ {subscription.status}
48
+ </span>
49
+ </div>
50
+
51
+ <div className="space-y-sm mb-lg">
52
+ <div className="flex justify-between">
53
+ <span className="sf-caption text-apple-label-secondary">{t('subscriptions.monthly_cost')}</span>
54
+ <span className="sf-body text-white">${price}</span>
55
+ </div>
56
+ {subscription.seatCount && (
57
+ <div className="flex justify-between">
58
+ <span className="sf-caption text-apple-label-secondary">{t('subscriptions.active_seats')}</span>
59
+ <span className="sf-body text-white">{subscription.seatCount}</span>
60
+ </div>
61
+ )}
62
+ <div className="flex justify-between">
63
+ <span className="sf-caption text-apple-label-secondary">{t('subscriptions.renews')}</span>
64
+ <span className="sf-body text-white">{renewDate.toLocaleDateString()}</span>
65
+ </div>
66
+ </div>
67
+
68
+ <button className="w-full px-md py-sm bg-white/10 hover:bg-white/20 text-white rounded-apple-md sf-body transition-colors cursor-pointer">
69
+ {t('subscriptions.manage')}
70
+ </button>
71
+ </div>
72
+ )
73
+ }
@@ -0,0 +1,53 @@
1
+ import React from 'react'
2
+ import { useTranslation } from 'react-i18next'
3
+ import { X } from 'lucide-react'
4
+ import { Plan } from '../../types/billing'
5
+
6
+ interface UpgradeModalProps {
7
+ visible: boolean
8
+ plan: Plan | null
9
+ onConfirm: () => void
10
+ onCancel: () => void
11
+ }
12
+
13
+ export default function UpgradeModal({ visible, plan, onConfirm, onCancel }: UpgradeModalProps) {
14
+ const { t } = useTranslation('billing')
15
+ if (!visible || !plan) return null
16
+
17
+ const price = plan.monthlyPrice.toFixed(2)
18
+
19
+ return (
20
+ <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50"
21
+ role="dialog" aria-modal="true" aria-labelledby="upgrade-title"
22
+ onKeyDown={(e) => { if (e.key === 'Escape') onCancel() }}>
23
+ <div className="bg-apple-bg rounded-xl border border-white/[0.06] p-6 max-w-md w-full mx-4">
24
+ <div className="flex items-center justify-between mb-4">
25
+ <h3 id="upgrade-title" className="text-lg font-semibold text-white">
26
+ {t('upgrade.title', { plan: plan.name })}
27
+ </h3>
28
+ <button type="button" onClick={onCancel} aria-label={t('upgrade.cancel')}
29
+ className="text-zinc-400 hover:text-white cursor-pointer">
30
+ <X size={20} />
31
+ </button>
32
+ </div>
33
+ <p className="text-sm text-zinc-400 mb-6">{t('upgrade.description', { price })}</p>
34
+ <div className="bg-white/5 rounded-lg p-4 mb-6">
35
+ <p className="text-xs text-zinc-400">{t('upgrade.new_limit')}</p>
36
+ <p className="text-xl font-bold text-white">
37
+ {t('upgrade.screenings_mo', { count: ((plan.limits['screenings'] ?? 0) / 1000).toFixed(0) })}
38
+ </p>
39
+ </div>
40
+ <div className="flex flex-col-reverse sm:flex-row gap-3">
41
+ <button type="button" onClick={onCancel}
42
+ className="flex-1 px-4 py-2 border border-white/10 text-white rounded-lg hover:bg-white/5 transition-colors cursor-pointer">
43
+ {t('upgrade.cancel')}
44
+ </button>
45
+ <button type="button" onClick={onConfirm}
46
+ className="flex-1 px-4 py-2 bg-apple-blue hover:bg-apple-blue/80 text-white rounded-lg transition-colors font-medium cursor-pointer">
47
+ {t('upgrade.confirm')}
48
+ </button>
49
+ </div>
50
+ </div>
51
+ </div>
52
+ )
53
+ }
@@ -0,0 +1,37 @@
1
+ import React, { useState, useEffect } from 'react'
2
+ import { useTranslation } from 'react-i18next'
3
+ import { api } from '../../api/client'
4
+
5
+ interface UsagePoint { month: string; screenings: number }
6
+
7
+ export default function UsageHistory() {
8
+ const { t } = useTranslation('billing')
9
+ const [history, setHistory] = useState<UsagePoint[]>([])
10
+ useEffect(() => {
11
+ api.get<UsagePoint[]>('/billing/usage/history')
12
+ .then(setHistory)
13
+ .catch(() => setHistory([]))
14
+ }, [])
15
+
16
+ const maxScreenings = Math.max(...history.map(h => h.screenings), 1)
17
+
18
+ return (
19
+ <div className="rounded-apple-lg border border-white/10 bg-white/5 p-lg">
20
+ <h3 className="sf-body font-medium text-white mb-lg">{t('usage.history_title')}</h3>
21
+ <div className="h-64 flex items-end justify-between gap-sm">
22
+ {history.map((point, i) => (
23
+ <div key={i} className="flex-1 flex flex-col items-center gap-sm">
24
+ <div className="w-full h-64 flex flex-col items-center justify-end">
25
+ <div
26
+ className="w-full bg-gradient-to-t from-apple-blue to-blue-400 rounded-t-apple-md"
27
+ style={{ height: `${(point.screenings / maxScreenings) * 100}%` }}
28
+ title={`${point.screenings} screenings`}
29
+ />
30
+ </div>
31
+ <span className="sf-caption text-xs">{point.month}</span>
32
+ </div>
33
+ ))}
34
+ </div>
35
+ </div>
36
+ )
37
+ }
@@ -0,0 +1,26 @@
1
+ import React from 'react'
2
+
3
+ interface UsageMeterProps {
4
+ label: string
5
+ current: number
6
+ max: number
7
+ }
8
+
9
+ export default function UsageMeter({ label, current, max }: UsageMeterProps) {
10
+ const percentage = (current / max) * 100
11
+ const color = percentage < 80 ? 'bg-green-600' : percentage < 95 ? 'bg-amber-600' : 'bg-red-600'
12
+
13
+ return (
14
+ <div>
15
+ <div className="flex items-center justify-between mb-2">
16
+ <label className="text-sm font-medium text-zinc-400">{label}</label>
17
+ <span className="text-xs text-zinc-400">
18
+ {current.toLocaleString()} / {max.toLocaleString()} ({percentage.toFixed(1)}%)
19
+ </span>
20
+ </div>
21
+ <div className="w-full h-2 bg-white/10 rounded-full overflow-hidden">
22
+ <div className={`h-full ${color} transition-all duration-500`} style={{ width: `${Math.min(percentage, 100)}%` }} />
23
+ </div>
24
+ </div>
25
+ )
26
+ }
@@ -0,0 +1,38 @@
1
+ import React, { useEffect, useState } from 'react'
2
+ import { useTranslation } from 'react-i18next'
3
+ import UsageMeter from './UsageMeter'
4
+ import { UsageRecord } from '../../types/billing'
5
+ import { getUsage } from '../../api/billing'
6
+
7
+ export default function UsageOverview() {
8
+ const { t } = useTranslation('billing')
9
+ const [usage, setUsage] = useState<UsageRecord | null>(null)
10
+ const [loading, setLoading] = useState(true)
11
+
12
+ useEffect(() => {
13
+ getUsage('dashboard').then(setUsage).finally(() => setLoading(false))
14
+ }, [])
15
+
16
+ if (loading || !usage) return <div className="animate-pulse h-32 bg-white/5 rounded-xl" />
17
+
18
+ return (
19
+ <div className="bg-white/[0.02] rounded-xl border border-white/[0.06] p-6 space-y-6">
20
+ <div>
21
+ <h3 className="text-lg font-semibold text-white mb-1">{t('usage.current_usage')}</h3>
22
+ <p className="text-sm text-zinc-400">{usage.product}</p>
23
+ </div>
24
+
25
+ {usage.metrics.map(m => (
26
+ <UsageMeter key={m.name} label={m.name} current={m.current} max={m.limit} />
27
+ ))}
28
+
29
+ {usage.metrics.some(m => m.current > m.limit * 0.9) && (
30
+ <div className="bg-amber-900/20 border border-amber-800 rounded-lg p-4">
31
+ <p className="text-sm text-amber-200">
32
+ {t('usage.warning', { percent: 90 })}
33
+ </p>
34
+ </div>
35
+ )}
36
+ </div>
37
+ )
38
+ }
@@ -0,0 +1,25 @@
1
+ import React from 'react'
2
+ import { useTranslation } from 'react-i18next'
3
+
4
+ export default function PrivacyPolicy() {
5
+ const { t } = useTranslation('legal')
6
+ const sections = t('privacy.sections', { returnObjects: true }) as Array<{
7
+ title: string; body: string
8
+ }>
9
+
10
+ return (
11
+ <article className="max-w-3xl mx-auto px-6 py-20">
12
+ <h1 className="text-4xl font-bold text-white mb-2">{t('privacy.title')}</h1>
13
+ <p className="text-sm text-zinc-400 mb-12">{t('privacy.last_updated')}</p>
14
+
15
+ <div className="space-y-8 text-zinc-400 leading-relaxed">
16
+ {sections.map((section, i) => (
17
+ <section key={i}>
18
+ <h2 className="text-lg font-semibold text-white mb-3">{section.title}</h2>
19
+ <p>{section.body}</p>
20
+ </section>
21
+ ))}
22
+ </div>
23
+ </article>
24
+ )
25
+ }
@@ -0,0 +1,25 @@
1
+ import React from 'react'
2
+ import { useTranslation } from 'react-i18next'
3
+
4
+ export default function TermsOfService() {
5
+ const { t } = useTranslation('legal')
6
+ const sections = t('terms.sections', { returnObjects: true }) as Array<{
7
+ title: string; body: string
8
+ }>
9
+
10
+ return (
11
+ <article className="max-w-3xl mx-auto px-6 py-20">
12
+ <h1 className="text-4xl font-bold text-white mb-2">{t('terms.title')}</h1>
13
+ <p className="text-sm text-zinc-400 mb-12">{t('terms.last_updated')}</p>
14
+
15
+ <div className="space-y-8 text-zinc-400 leading-relaxed">
16
+ {sections.map((section, i) => (
17
+ <section key={i}>
18
+ <h2 className="text-lg font-semibold text-white mb-3">{section.title}</h2>
19
+ <p>{section.body}</p>
20
+ </section>
21
+ ))}
22
+ </div>
23
+ </article>
24
+ )
25
+ }
@@ -0,0 +1,24 @@
1
+ import React from 'react'
2
+ import { useTranslation } from 'react-i18next'
3
+ import { Star } from 'lucide-react'
4
+
5
+ export function BundleCallout() {
6
+ const { t } = useTranslation('marketing')
7
+
8
+ return (
9
+ <div className="bg-gradient-to-r from-apple-blue/20 to-purple-600/20 border border-apple-blue/30 rounded-apple-lg p-lg mx-auto max-w-2xl">
10
+ <div className="flex items-start gap-md">
11
+ <Star className="w-5 h-5 text-apple-blue flex-shrink-0 mt-sm" />
12
+ <div>
13
+ <h3 className="sf-headline text-white mb-xs">{t('bundle.title')}</h3>
14
+ <p className="sf-body text-apple-label-secondary mb-lg">
15
+ {t('bundle.description')}
16
+ </p>
17
+ <button type="button" className="px-lg py-sm bg-apple-blue hover:bg-apple-blue/80 text-white rounded-apple-md sf-body font-medium transition-colors">
18
+ {t('bundle.cta')}
19
+ </button>
20
+ </div>
21
+ </div>
22
+ </div>
23
+ )
24
+ }
@@ -0,0 +1,48 @@
1
+ import { useNavigate } from 'react-router-dom'
2
+ import { motion } from 'framer-motion'
3
+ import { FadeUp } from './animations'
4
+
5
+ export default function CTASection() {
6
+ const navigate = useNavigate()
7
+
8
+ return (
9
+ <section className="py-28 px-4">
10
+ <div className="max-w-3xl mx-auto text-center relative">
11
+ <div className="absolute -inset-20 rounded-3xl opacity-20 pointer-events-none"
12
+ style={{ background: 'radial-gradient(ellipse, rgba(0,184,148,0.15), transparent 70%)' }} />
13
+ <FadeUp>
14
+ <div className="relative rounded-3xl p-8 sm:p-14 overflow-hidden"
15
+ style={{
16
+ background: 'linear-gradient(135deg, #F8FAFC 0%, #F1F5F9 100%)',
17
+ border: '1px solid rgba(0,0,0,0.06)',
18
+ boxShadow: '0 20px 60px rgba(0,0,0,0.06)',
19
+ }}>
20
+ <h2 className="text-3xl sm:text-4xl lg:text-5xl font-bold text-[#0F172A] mb-5"
21
+ style={{ letterSpacing: '-0.03em' }}>
22
+ Ready to modernize your compliance?
23
+ </h2>
24
+ <p className="text-[#64748B] mb-10 max-w-lg mx-auto text-[15px]">
25
+ 100 free screenings/month. No credit card required.
26
+ Deploy in minutes, not months.
27
+ </p>
28
+ <div className="flex flex-col sm:flex-row gap-4 justify-center">
29
+ <motion.button type="button" onClick={() => navigate('/signup')}
30
+ className="px-8 py-4 bg-gradient-to-r from-[#00B894] to-[#059669] text-white font-semibold rounded-xl text-sm cursor-pointer"
31
+ whileHover={{ scale: 1.04, boxShadow: '0 8px 30px rgba(0,184,148,0.35)' }}
32
+ whileTap={{ scale: 0.97 }}>
33
+ Start Free
34
+ </motion.button>
35
+ <motion.button type="button"
36
+ onClick={() => window.open('https://calendly.com/amliq', '_blank')}
37
+ className="px-8 py-4 bg-gradient-to-r from-[#6366F1] to-[#8B5CF6] text-white font-semibold rounded-xl text-sm cursor-pointer"
38
+ whileHover={{ scale: 1.04, boxShadow: '0 8px 30px rgba(99,102,241,0.3)' }}
39
+ whileTap={{ scale: 0.97 }}>
40
+ Book a Demo
41
+ </motion.button>
42
+ </div>
43
+ </div>
44
+ </FadeUp>
45
+ </div>
46
+ </section>
47
+ )
48
+ }
@@ -0,0 +1,37 @@
1
+ import { motion } from 'framer-motion'
2
+ import { FadeUp, Parallax } from './animations'
3
+
4
+ export default function CaseStudy() {
5
+ return (
6
+ <section className="py-28 px-4 relative overflow-hidden">
7
+ <Parallax speed={0.2}>
8
+ <div className="absolute inset-0 opacity-30"
9
+ style={{ background: 'radial-gradient(ellipse at 50% 50%, rgba(0,184,148,0.08), transparent 60%)' }} />
10
+ </Parallax>
11
+ <div className="max-w-3xl mx-auto text-center relative z-10">
12
+ <FadeUp>
13
+ <p className="text-xs uppercase tracking-[0.2em] text-[#00B894] font-semibold mb-10">
14
+ Case Study
15
+ </p>
16
+ <blockquote>
17
+ <motion.p
18
+ className="text-2xl sm:text-3xl lg:text-4xl font-bold leading-snug mb-10 text-[#0F172A]"
19
+ style={{ letterSpacing: '-0.02em' }}>
20
+ &ldquo;AMLIQ reduced our false positives by 70% while cutting screening costs by 80%.&rdquo;
21
+ </motion.p>
22
+ </blockquote>
23
+ <div className="flex items-center justify-center gap-4">
24
+ <div className="w-12 h-12 rounded-full flex items-center justify-center text-sm font-bold
25
+ bg-gradient-to-br from-[#00B894] to-[#059669] text-white">
26
+ JR
27
+ </div>
28
+ <div className="text-left">
29
+ <p className="text-sm font-semibold text-[#0F172A]">James Richardson</p>
30
+ <p className="text-sm text-[#94A3B8]">Head of Compliance, Meridian Fintech</p>
31
+ </div>
32
+ </div>
33
+ </FadeUp>
34
+ </div>
35
+ </section>
36
+ )
37
+ }
@@ -0,0 +1,66 @@
1
+ import { Check, X } from 'lucide-react'
2
+
3
+ type Val = true | false | string
4
+ interface Row { feature: string; amliq: Val; worldCheck: Val; dowJones: Val; manual: Val }
5
+
6
+ const rows: Row[] = [
7
+ { feature: 'Starting Price', amliq: '$299/mo', worldCheck: '$15K+/yr', dowJones: '$10K+/yr', manual: '$50K+/yr' },
8
+ { feature: 'Screening Speed', amliq: '<50ms', worldCheck: '200-500ms', dowJones: '300-800ms', manual: 'Hours' },
9
+ { feature: 'AI Matching Layers', amliq: '6 layers', worldCheck: '2 layers', dowJones: '1 layer', manual: '0' },
10
+ { feature: 'Custom Thresholds', amliq: true, worldCheck: false, dowJones: false, manual: false },
11
+ { feature: 'Explainable Results', amliq: true, worldCheck: false, dowJones: false, manual: true },
12
+ { feature: 'Self-Hosted Option', amliq: true, worldCheck: false, dowJones: false, manual: true },
13
+ { feature: 'REST API', amliq: true, worldCheck: true, dowJones: true, manual: false },
14
+ ]
15
+
16
+ function Cell({ val, highlight }: { val: Val; highlight?: boolean }) {
17
+ if (val === true) return <Check size={16} className={highlight ? 'text-[#22C55E] mx-auto' : 'text-emerald-700 mx-auto'} style={highlight ? { filter: 'drop-shadow(0 0 4px rgba(34,197,94,0.5))' } : {}} />
18
+ if (val === false) return <X size={16} className="text-[#EF4444]/40 mx-auto" />
19
+ return <span className={`text-xs ${highlight ? 'text-cyan-300 font-semibold' : 'text-[#94A3B8]'}`}>{val}</span>
20
+ }
21
+
22
+ export default function ComparisonTable() {
23
+ const heads = ['Feature', 'AMLIQ', 'World-Check', 'Dow Jones', 'Manual']
24
+
25
+ return (
26
+ <section className="py-24 px-4">
27
+ <div className="max-w-5xl mx-auto">
28
+ <p className="text-sm text-cyan-400 font-mono text-center mb-3 uppercase tracking-widest">
29
+ // comparison
30
+ </p>
31
+ <h2 className="text-3xl sm:text-4xl font-bold text-[#F8FAFC] text-center mb-12">
32
+ See how AMLIQ compares
33
+ </h2>
34
+
35
+ <div className="overflow-x-auto -mx-4 px-4 sm:mx-0 sm:px-0 rounded-xl border border-cyan-400/10" style={{
36
+ background: 'rgba(255,255,255,0.01)',
37
+ backgroundImage: 'repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(0,255,255,0.01) 2px, rgba(0,255,255,0.01) 4px)',
38
+ }}>
39
+ <table className="w-full min-w-[540px]">
40
+ <thead>
41
+ <tr className="border-b border-cyan-400/10">
42
+ {heads.map((h, i) => (
43
+ <th key={h} className={`py-4 px-5 text-sm font-mono ${i === 1 ? 'text-cyan-400' : 'text-[#94A3B8]'} ${i === 0 ? 'text-left sticky left-0 bg-[#020617] z-10' : 'text-center'}`}
44
+ style={i === 1 ? { textShadow: '0 0 10px rgba(0,255,255,0.3)' } : {}}>
45
+ {h}
46
+ </th>
47
+ ))}
48
+ </tr>
49
+ </thead>
50
+ <tbody>
51
+ {rows.map((r, i) => (
52
+ <tr key={r.feature} className={`border-b border-white/[0.03] ${i % 2 === 1 ? 'bg-white/[0.01]' : ''}`}>
53
+ <td className="py-3 px-5 text-sm text-[#94A3B8] sticky left-0 bg-[#020617] z-10 font-mono text-xs">{r.feature}</td>
54
+ <td className="py-3 text-center bg-cyan-400/[0.02]"><Cell val={r.amliq} highlight /></td>
55
+ <td className="py-3 text-center"><Cell val={r.worldCheck} /></td>
56
+ <td className="py-3 text-center"><Cell val={r.dowJones} /></td>
57
+ <td className="py-3 text-center"><Cell val={r.manual} /></td>
58
+ </tr>
59
+ ))}
60
+ </tbody>
61
+ </table>
62
+ </div>
63
+ </div>
64
+ </section>
65
+ )
66
+ }
@@ -0,0 +1,55 @@
1
+ import { Shield, Zap, Brain, Globe, DollarSign, Eye } from 'lucide-react'
2
+ import { motion } from 'framer-motion'
3
+ import { FadeUp } from './animations'
4
+
5
+ const advantages = [
6
+ { icon: Zap, stat: '200x', label: 'Faster', detail: 'Sub-1ms in-memory screening vs 200ms industry average', gradient: 'from-amber-400 to-orange-500' },
7
+ { icon: Brain, stat: '92%', label: 'Fewer false positives', detail: 'LLM cascade validated by Federal Reserve 2025 research', gradient: 'from-violet-400 to-purple-500' },
8
+ { icon: Globe, stat: '2.2M+', label: 'Entity profiles', detail: 'Growing to 5M+ from 328 global sources + Wikidata + AI', gradient: 'from-blue-400 to-cyan-500' },
9
+ { icon: Eye, stat: '6', label: 'Matching layers', detail: 'Exact, Fuzzy, Phonetic, Token, Embedding, Graph — fully explainable', gradient: 'from-emerald-400 to-teal-500' },
10
+ { icon: DollarSign, stat: '100x', label: 'Cheaper', detail: '$299/mo vs $30K-500K/year for legacy providers', gradient: 'from-green-400 to-emerald-500' },
11
+ { icon: Shield, stat: '30+', label: 'Country lists', detail: 'OFAC, UN, EU, UK, IL NBCTF + 25 country-specific sanctions lists', gradient: 'from-rose-400 to-pink-500' },
12
+ ]
13
+
14
+ export default function CompetitiveEdge() {
15
+ return (
16
+ <section className="py-28 px-4">
17
+ <div className="max-w-6xl mx-auto">
18
+ <FadeUp>
19
+ <div className="text-center mb-20">
20
+ <p className="text-xs uppercase tracking-[0.2em] text-[#00B894] font-semibold mb-4">
21
+ Why compliance teams switch to AMLIQ
22
+ </p>
23
+ <h2 className="text-3xl sm:text-4xl lg:text-5xl font-bold text-[#0F172A]"
24
+ style={{ letterSpacing: '-0.03em' }}>
25
+ Outperform legacy screening
26
+ </h2>
27
+ </div>
28
+ </FadeUp>
29
+
30
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5">
31
+ {advantages.map((a, i) => (
32
+ <FadeUp key={a.label} delay={i * 0.08}>
33
+ <motion.div
34
+ className="relative p-6 rounded-2xl cursor-default overflow-hidden group"
35
+ style={{ background: '#FAFBFC', border: '1px solid rgba(0,0,0,0.06)' }}
36
+ whileHover={{ y: -4, boxShadow: '0 12px 40px rgba(0,0,0,0.08)' }}
37
+ transition={{ type: 'spring', stiffness: 300, damping: 20 }}>
38
+ <div className="flex items-center gap-3 mb-4">
39
+ <div className={`w-10 h-10 rounded-xl flex items-center justify-center bg-gradient-to-br ${a.gradient} opacity-90`}>
40
+ <a.icon size={18} color="white" />
41
+ </div>
42
+ <span className="text-3xl font-extrabold bg-gradient-to-r from-[#00B894] to-[#059669] bg-clip-text text-transparent">
43
+ {a.stat}
44
+ </span>
45
+ </div>
46
+ <p className="text-[#0F172A] font-semibold text-sm mb-1">{a.label}</p>
47
+ <p className="text-[#94A3B8] text-xs leading-relaxed">{a.detail}</p>
48
+ </motion.div>
49
+ </FadeUp>
50
+ ))}
51
+ </div>
52
+ </div>
53
+ </section>
54
+ )
55
+ }