@syndew/oluso-plugin-verification 0.0.0-stage → 0.0.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"syndew-oluso-plugin-verification.mjs","sources":["../../../../../libs/plugins/verification/src/lib/services/verification.service.ts","../../../../../libs/plugins/verification/src/lib/components/verification-settings-tab.component.ts","../../../../../libs/plugins/verification/src/lib/verification.routes.ts","../../../../../libs/plugins/verification/src/lib/verification.plugin.ts","../../../../../libs/plugins/verification/src/lib/models/verification.model.ts","../../../../../libs/plugins/verification/src/lib/pages/businesses/businesses-page.component.ts","../../../../../libs/plugins/verification/src/lib/pages/business-detail/business-detail-page.component.ts","../../../../../libs/plugins/verification/src/lib/pages/review-queue/review-queue-page.component.ts","../../../../../libs/plugins/verification/src/index.ts","../../../../../libs/plugins/verification/src/syndew-oluso-plugin-verification.ts"],"sourcesContent":["import { Injectable, inject } from '@angular/core';\nimport { HttpParams } from '@angular/common/http';\nimport { Observable } from 'rxjs';\nimport { ApiService } from '@syndew/oluso-admin-ui';\nimport {\n BusinessApplicant,\n BusinessReviewQueue,\n BusinessVerificationCountry,\n IdentityVerificationSettings,\n ReviewDecision,\n UpdateIdentityVerificationSettingsRequest,\n VerificationConnectionTestResult,\n VerificationProviderInfo,\n VerifiedBusinessDetail,\n VerifiedBusinessList,\n} from '../models/verification.model';\n\n/** Business (KYB) verification: providers, settings, businesses, applicants and reviews (api/admin/business-verification). */\n@Injectable({ providedIn: 'root' })\nexport class VerificationService {\n private readonly api = inject(ApiService);\n private readonly endpoint = '/business-verification';\n\n getProviders(): Observable<VerificationProviderInfo[]> {\n return this.api.get<VerificationProviderInfo[]>(`${this.endpoint}/providers`);\n }\n\n getCountries(): Observable<BusinessVerificationCountry[]> {\n return this.api.get<BusinessVerificationCountry[]>(`${this.endpoint}/countries`);\n }\n\n getSettings(): Observable<IdentityVerificationSettings> {\n return this.api.get<IdentityVerificationSettings>(`${this.endpoint}/settings`);\n }\n\n updateSettings(input: UpdateIdentityVerificationSettingsRequest): Observable<IdentityVerificationSettings> {\n return this.api.put<IdentityVerificationSettings>(`${this.endpoint}/settings`, input);\n }\n\n testProvider(providerId: string): Observable<VerificationConnectionTestResult> {\n return this.api.post<VerificationConnectionTestResult>(`${this.endpoint}/providers/${encodeURIComponent(providerId)}/test`, {});\n }\n\n listBusinesses(status: string, search: string, page: number, pageSize: number): Observable<VerifiedBusinessList> {\n const params: Record<string, string | number> = { page, pageSize };\n if (status) params['status'] = status;\n if (search) params['search'] = search;\n return this.api.get<VerifiedBusinessList>(`${this.endpoint}/businesses`, params);\n }\n\n getBusiness(id: string): Observable<VerifiedBusinessDetail> {\n return this.api.get<VerifiedBusinessDetail>(`${this.endpoint}/businesses/${encodeURIComponent(id)}`);\n }\n\n reviewQueue(page: number, pageSize: number): Observable<BusinessReviewQueue> {\n return this.api.get<BusinessReviewQueue>(`${this.endpoint}/review-queue`, { page, pageSize });\n }\n\n reviewBusiness(id: string, decision: ReviewDecision, note?: string): Observable<VerifiedBusinessDetail> {\n return this.api.post<VerifiedBusinessDetail>(`${this.endpoint}/businesses/${encodeURIComponent(id)}/review`, { decision, note });\n }\n\n reviewApplicant(id: string, decision: ReviewDecision, note?: string): Observable<BusinessApplicant> {\n return this.api.post<BusinessApplicant>(`${this.endpoint}/applicants/${encodeURIComponent(id)}/review`, { decision, note });\n }\n\n refreshApplicant(id: string): Observable<BusinessApplicant> {\n return this.api.post<BusinessApplicant>(`${this.endpoint}/applicants/${encodeURIComponent(id)}/refresh`, {});\n }\n\n downloadDocument(applicantId: string, documentId: string): Observable<Blob> {\n return this.api.getBlob(\n `${this.endpoint}/applicants/${encodeURIComponent(applicantId)}/documents/${encodeURIComponent(documentId)}`,\n new HttpParams(),\n );\n }\n}\n","import { Component, OnInit, inject, signal } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { forkJoin } from 'rxjs';\nimport { ButtonComponent, IconComponent, ToastService, extractErrorMessage } from '@syndew/oluso-ui-core';\nimport { VerificationService } from '../services/verification.service';\nimport {\n BusinessVerificationCountry,\n IdentityVerificationSettings,\n VerificationConnectionTestResult,\n VerificationEnvironment,\n VerificationProviderInfo,\n} from '../models/verification.model';\n\ninterface ProviderForm {\n info: VerificationProviderInfo;\n enabled: boolean;\n environment: VerificationEnvironment;\n credentials: Record<string, string>;\n storedSecrets: string[];\n options: Record<string, string>;\n}\n\n@Component({\n selector: 'verification-settings-tab',\n standalone: true,\n imports: [CommonModule, FormsModule, ButtonComponent, IconComponent],\n template: `\n @if (loading()) {\n <div class=\"flex items-center justify-center py-12\">\n <div class=\"animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600\"></div>\n </div>\n } @else if (loadError()) {\n <div class=\"bg-red-50 border border-red-200 rounded-md p-4\"><p class=\"text-sm text-red-600\">{{ loadError() }}</p></div>\n } @else {\n <form (ngSubmit)=\"save()\" class=\"space-y-6\">\n <div class=\"bg-blue-50 border border-blue-200 rounded-lg p-4\">\n <div class=\"flex items-start gap-3\">\n <ui-icon name=\"information-circle\" class=\"h-5 w-5 text-blue-500 mt-0.5 flex-shrink-0\" />\n <div class=\"text-sm text-blue-800\">\n <p class=\"font-medium\">Business verification (KYB)</p>\n <p class=\"mt-1\">\n The Business Verification journey step checks a business against its company registry, or through the\n provider's hosted flow, using the provider you route its country to. Credentials are stored encrypted and\n never shown again. Use sandbox keys while testing: sandbox lookups use test data and aren't billed.\n </p>\n </div>\n </div>\n </div>\n\n @for (p of providers(); track p.info.id) {\n <div class=\"rounded-lg border border-gray-200 p-4 space-y-4\">\n <div class=\"flex flex-wrap items-center justify-between gap-3\">\n <div>\n <p class=\"text-sm font-semibold text-gray-900\">{{ p.info.displayName }}</p>\n <p class=\"text-xs text-gray-500\">Company registries: {{ countryNames(p.info.businessCountries) }}</p>\n @if (p.info.hostedCountries.length > 0) {\n <p class=\"text-xs text-gray-500\">Hosted verification: {{ countryNames(p.info.hostedCountries) }}</p>\n }\n </div>\n <label class=\"flex items-center gap-2\">\n <input type=\"checkbox\" [(ngModel)]=\"p.enabled\" [name]=\"p.info.id + '-enabled'\" class=\"rounded border-gray-300 text-primary-600\" />\n <span class=\"text-sm text-gray-700\">Enabled</span>\n </label>\n </div>\n\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <div>\n <label class=\"block text-sm font-medium text-gray-700\" [for]=\"p.info.id + '-environment'\">Environment</label>\n <select [id]=\"p.info.id + '-environment'\" [(ngModel)]=\"p.environment\" [name]=\"p.info.id + '-environment'\" class=\"mt-1 w-full\">\n <option value=\"Sandbox\">Sandbox (test data, not billed)</option>\n <option value=\"Live\">Live (billed per lookup)</option>\n </select>\n <p class=\"mt-1 text-xs text-gray-500\">Switching environment clears stored secrets; enter the keys for the new one.</p>\n </div>\n\n @for (o of p.info.options; track o.name) {\n <div>\n <label class=\"block text-sm font-medium text-gray-700\" [for]=\"p.info.id + '-' + o.name\">{{ o.label }}</label>\n <select [id]=\"p.info.id + '-' + o.name\" [(ngModel)]=\"p.options[o.name]\" [name]=\"p.info.id + '-' + o.name\" class=\"mt-1 w-full\">\n @for (c of o.choices; track c) {\n <option [value]=\"c\">{{ c }}</option>\n }\n </select>\n @if (o.description) { <p class=\"mt-1 text-xs text-gray-500\">{{ o.description }}</p> }\n </div>\n }\n\n @for (f of p.info.credentialFields; track f.name) {\n <div>\n <label class=\"block text-sm font-medium text-gray-700\" [for]=\"p.info.id + '-' + f.name\">\n {{ f.label }}\n @if (!f.required) { <span class=\"text-xs font-normal text-gray-500\">(hosted verification only)</span> }\n </label>\n @if (f.secret) {\n <input type=\"password\" [id]=\"p.info.id + '-' + f.name\" [(ngModel)]=\"p.credentials[f.name]\" [name]=\"p.info.id + '-' + f.name\"\n class=\"mt-1 w-full\" autocomplete=\"new-password\"\n [placeholder]=\"p.storedSecrets.includes(f.name) ? 'Stored. Leave blank to keep.' : 'Not set'\" />\n } @else {\n <input type=\"text\" [id]=\"p.info.id + '-' + f.name\" [(ngModel)]=\"p.credentials[f.name]\" [name]=\"p.info.id + '-' + f.name\"\n class=\"mt-1 w-full\" autocomplete=\"off\" />\n }\n @if (f.description) { <p class=\"mt-1 text-xs text-gray-500\">{{ f.description }}</p> }\n </div>\n }\n </div>\n\n @if (p.info.webhookUrl) {\n <div class=\"rounded-md bg-gray-50 border border-gray-200 p-3\">\n <p class=\"text-xs font-medium text-gray-700\">Webhook URL for {{ p.info.displayName }}</p>\n <div class=\"mt-1 flex items-center gap-2\">\n <code class=\"text-xs break-all text-gray-800\">{{ p.info.webhookUrl }}</code>\n <ui-button type=\"button\" variant=\"ghost\" size=\"sm\" (click)=\"copy(p.info.webhookUrl)\">Copy</ui-button>\n </div>\n <p class=\"mt-1 text-xs text-gray-500\">Give this to the provider for hosted verification results. Requests are accepted only when their signature verifies with the webhook secret above.</p>\n </div>\n }\n\n <div class=\"flex items-center gap-3\">\n <ui-button type=\"button\" variant=\"secondary\" (click)=\"test(p.info.id)\" [disabled]=\"testing() === p.info.id\">\n {{ testing() === p.info.id ? 'Testing…' : 'Test saved credentials' }}\n </ui-button>\n @if (testResults()[p.info.id]; as r) {\n <span class=\"text-sm\" [class.text-green-700]=\"r.success\" [class.text-red-600]=\"!r.success\">{{ r.message }}</span>\n }\n </div>\n </div>\n }\n\n <div class=\"rounded-lg border border-gray-200 p-4 space-y-4\">\n <div>\n <p class=\"text-sm font-semibold text-gray-900\">Country routing</p>\n <p class=\"text-xs text-gray-500\">Which provider verifies businesses registered in each country. A country with no provider can't be verified; the journey step fails instead of trying another provider.</p>\n </div>\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n @for (c of countries(); track c.code) {\n <div>\n <label class=\"block text-sm font-medium text-gray-700\" [for]=\"'route-' + c.code\">{{ c.name }} ({{ c.code }})</label>\n <select [id]=\"'route-' + c.code\" [(ngModel)]=\"routing[c.code]\" [name]=\"'route-' + c.code\" class=\"mt-1 w-full\">\n <option value=\"\">Not routed</option>\n @for (id of c.providerIds; track id) {\n <option [value]=\"id\">{{ providerName(id) }}</option>\n }\n </select>\n </div>\n }\n </div>\n </div>\n\n <div class=\"rounded-lg border border-gray-200 p-4 space-y-2 max-w-md\">\n <label class=\"block text-sm font-semibold text-gray-900\" for=\"reuse-days\">Reuse registry results for (days)</label>\n <input id=\"reuse-days\" type=\"number\" min=\"0\" max=\"365\" [(ngModel)]=\"reuseDays\" name=\"reuseDays\" class=\"w-32\" />\n <p class=\"text-xs text-gray-500\">When the same registration number is verified again within this many days, the earlier registry result is reused instead of paying for a new lookup. 0 always looks up again.</p>\n </div>\n\n <div class=\"flex items-center gap-3\">\n <ui-button type=\"submit\" [disabled]=\"saving()\">{{ saving() ? 'Saving…' : 'Save' }}</ui-button>\n </div>\n </form>\n }\n `,\n})\nexport class VerificationSettingsTabComponent implements OnInit {\n private readonly service = inject(VerificationService);\n private readonly toast = inject(ToastService);\n\n readonly loading = signal(true);\n readonly saving = signal(false);\n readonly testing = signal<string | null>(null);\n readonly loadError = signal<string | null>(null);\n readonly providers = signal<ProviderForm[]>([]);\n readonly countries = signal<BusinessVerificationCountry[]>([]);\n readonly testResults = signal<Record<string, VerificationConnectionTestResult>>({});\n\n routing: Record<string, string> = {};\n reuseDays = 0;\n\n ngOnInit(): void {\n forkJoin({\n providers: this.service.getProviders(),\n countries: this.service.getCountries(),\n settings: this.service.getSettings(),\n }).subscribe({\n next: ({ providers, countries, settings }) => {\n this.countries.set(countries);\n this.apply(providers, settings);\n this.loading.set(false);\n },\n error: (err) => {\n this.loadError.set(extractErrorMessage(err, 'Failed to load business verification settings'));\n this.loading.set(false);\n },\n });\n }\n\n countryNames(codes: string[]): string {\n return codes.map((code) => this.countries().find((c) => c.code === code)?.name ?? code).join(', ');\n }\n\n providerName(id: string): string {\n return this.providers().find((p) => p.info.id === id)?.info.displayName ?? id;\n }\n\n copy(text: string): void {\n navigator.clipboard.writeText(text).then(\n () => this.toast.success('Webhook URL copied'),\n () => this.toast.error('Copy failed; select the URL and copy it manually'),\n );\n }\n\n save(): void {\n if (this.saving()) return;\n this.saving.set(true);\n const businessRouting: Record<string, string> = {};\n for (const [country, providerId] of Object.entries(this.routing)) {\n if (providerId) businessRouting[country] = providerId;\n }\n this.service\n .updateSettings({\n providers: this.providers().map((p) => ({\n providerId: p.info.id,\n enabled: p.enabled,\n environment: p.environment,\n credentials: { ...p.credentials },\n options: { ...p.options },\n })),\n businessRouting,\n businessReuseMaxAgeDays: Number(this.reuseDays) || 0,\n })\n .subscribe({\n next: (settings) => {\n this.apply(this.providers().map((p) => p.info), settings);\n this.saving.set(false);\n this.toast.success('Business verification settings saved');\n },\n error: (err) => {\n this.saving.set(false);\n this.toast.error(extractErrorMessage(err, 'Failed to save business verification settings'));\n },\n });\n }\n\n test(providerId: string): void {\n this.testing.set(providerId);\n this.service.testProvider(providerId).subscribe({\n next: (result) => {\n this.testResults.update((all) => ({ ...all, [providerId]: result }));\n this.testing.set(null);\n },\n error: (err) => {\n this.testResults.update((all) => ({ ...all, [providerId]: { success: false, message: extractErrorMessage(err, 'Test failed') } }));\n this.testing.set(null);\n },\n });\n }\n\n private apply(infos: VerificationProviderInfo[], settings: IdentityVerificationSettings): void {\n this.providers.set(\n infos.map((info) => {\n const saved = settings.providers.find((p) => p.providerId === info.id);\n const credentials: Record<string, string> = {};\n for (const field of info.credentialFields) {\n credentials[field.name] = field.secret ? '' : saved?.credentials[field.name] ?? '';\n }\n const options: Record<string, string> = {};\n for (const option of info.options) {\n options[option.name] = saved?.options[option.name] ?? option.default;\n }\n return {\n info,\n enabled: saved?.enabled ?? false,\n environment: saved?.environment ?? 'Sandbox',\n credentials,\n storedSecrets: saved?.storedSecrets ?? [],\n options,\n };\n }),\n );\n this.routing = {};\n for (const c of this.countries()) {\n this.routing[c.code] = settings.businessRouting[c.code] ?? '';\n }\n this.reuseDays = settings.businessReuseMaxAgeDays;\n }\n}\n","import { Routes } from '@angular/router';\n\nexport const VERIFICATION_ROUTES: Routes = [\n {\n path: '',\n redirectTo: 'businesses',\n pathMatch: 'full',\n },\n {\n path: 'businesses',\n loadComponent: () => import('./pages/businesses/businesses-page.component').then((m) => m.BusinessesPageComponent),\n data: { title: 'Verified businesses', helpContext: 'admin.verification.businesses' },\n },\n {\n path: 'businesses/:id',\n loadComponent: () => import('./pages/business-detail/business-detail-page.component').then((m) => m.BusinessDetailPageComponent),\n data: { title: 'Business', helpContext: 'admin.verification.businesses' },\n },\n {\n path: 'review',\n loadComponent: () => import('./pages/review-queue/review-queue-page.component').then((m) => m.ReviewQueuePageComponent),\n data: { title: 'Verification review queue', helpContext: 'admin.verification.businesses' },\n },\n];\n","import { AdminPlugin, NavItem } from '@syndew/oluso-ui-core';\nimport { VerificationSettingsTabComponent } from './components/verification-settings-tab.component';\nimport { VERIFICATION_ROUTES } from './verification.routes';\n\n/**\n * Business (KYB) verification admin plugin: verified businesses with the people acting\n * for them and their documents, the review queue, and the provider settings tab.\n */\nexport const VERIFICATION_PLUGIN: AdminPlugin = {\n name: 'oluso-verification',\n version: '1.0.0',\n\n routes: [\n {\n path: 'verification',\n children: VERIFICATION_ROUTES,\n },\n ],\n\n navItems: [\n {\n id: 'verification',\n label: 'Business Verification',\n path: '/verification',\n icon: 'building-office-2',\n order: 15,\n permission: 'business_verification.read',\n mergeIntoPath: '/experience',\n children: [\n { label: 'Businesses', path: '/verification/businesses', icon: 'building-office-2', order: 0 },\n { label: 'Review queue', path: '/verification/review', icon: 'inbox', order: 10 },\n ],\n } as NavItem,\n ],\n\n settingsTabs: [\n {\n id: 'business-verification',\n label: 'Business verification',\n icon: 'building-office-2',\n component: VerificationSettingsTabComponent,\n order: 39,\n permission: 'business_verification.manage',\n },\n ],\n};\n","/** A verification vendor the platform supports (api/admin/business-verification/providers). */\nexport interface VerificationProviderInfo {\n id: string;\n displayName: string;\n businessCountries: string[];\n /** Countries its hosted (provider-run) business verification covers. */\n hostedCountries: string[];\n /** The URL to give the provider for webhooks, on this organisation's host. */\n webhookUrl?: string | null;\n credentialFields: VerificationCredentialField[];\n options: VerificationProviderOption[];\n}\n\nexport interface VerificationCredentialField {\n name: string;\n label: string;\n secret: boolean;\n description?: string | null;\n /** Needed to enable the provider; the others serve hosted verification only. */\n required: boolean;\n}\n\nexport interface VerificationProviderOption {\n name: string;\n label: string;\n choices: string[];\n default: string;\n description?: string | null;\n}\n\nexport interface BusinessVerificationCountry {\n code: string;\n name: string;\n providerIds: string[];\n routedProviderId?: string | null;\n providerName: string;\n}\n\nexport type VerificationEnvironment = 'Sandbox' | 'Live';\n\n/** Secrets are never returned; storedSecrets names the ones that are set. */\nexport interface ConfiguredVerificationProvider {\n providerId: string;\n enabled: boolean;\n environment: VerificationEnvironment;\n credentials: Record<string, string>;\n storedSecrets: string[];\n options: Record<string, string>;\n}\n\nexport interface IdentityVerificationSettings {\n providers: ConfiguredVerificationProvider[];\n businessRouting: Record<string, string>;\n businessReuseMaxAgeDays: number;\n}\n\nexport interface UpdateVerificationProviderRequest {\n providerId: string;\n enabled: boolean;\n environment: VerificationEnvironment;\n /** A blank secret keeps the stored value. */\n credentials: Record<string, string>;\n options: Record<string, string>;\n}\n\nexport interface UpdateIdentityVerificationSettingsRequest {\n providers: UpdateVerificationProviderRequest[];\n businessRouting: Record<string, string>;\n businessReuseMaxAgeDays: number;\n}\n\nexport interface VerificationConnectionTestResult {\n success: boolean;\n message: string;\n}\n\nexport type BusinessVerificationStatus = 'pending' | 'in_review' | 'verified' | 'rejected' | 'failed';\n\nexport interface VerifiedBusinessSummary {\n id: string;\n status: BusinessVerificationStatus;\n statusReason?: string | null;\n country: string;\n registrationNumber: string;\n registeredName?: string | null;\n registryProviderId?: string | null;\n registryCheckedAt?: string | null;\n applicantCount: number;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface BusinessPerson {\n name: string;\n role?: string | null;\n}\n\nexport interface BusinessDocument {\n id: string;\n fileName: string;\n contentType: string;\n sizeBytes: number;\n createdAt: string;\n}\n\nexport interface BusinessApplicant {\n id: string;\n businessId: string;\n subjectId?: string | null;\n subjectName?: string | null;\n relationship: string;\n evidenceRequirement: string;\n status: BusinessVerificationStatus;\n statusReason?: string | null;\n statusMessage?: string | null;\n providedBusinessName?: string | null;\n nameMatched?: boolean | null;\n directorMatched?: boolean | null;\n matchedPersonName?: string | null;\n matchedPersonRole?: string | null;\n documents: BusinessDocument[];\n outcomeEmailRequested: boolean;\n createdAt: string;\n updatedAt: string;\n decidedAt?: string | null;\n reviewedBy?: string | null;\n reviewedAt?: string | null;\n reviewNote?: string | null;\n}\n\nexport interface BusinessCheck {\n id: string;\n applicantId?: string | null;\n kind: 'lookup' | 'reuse' | 'hosted_session' | 'review';\n outcome: string;\n providerId?: string | null;\n environment?: string | null;\n providerReference?: string | null;\n sessionReference?: string | null;\n message?: string | null;\n reusedFromCheckId?: string | null;\n originKind?: string | null;\n originId?: string | null;\n originName?: string | null;\n createdAt: string;\n completedAt?: string | null;\n}\n\nexport interface VerifiedBusinessDetail extends VerifiedBusinessSummary {\n registrationType?: string | null;\n statusMessage?: string | null;\n entityType?: string | null;\n registrationDate?: string | null;\n registeredAddress?: string | null;\n activityStatus: 'Unknown' | 'Active' | 'Inactive';\n rawStatus?: string | null;\n registryEnvironment?: string | null;\n people: BusinessPerson[];\n decidedAt?: string | null;\n reviewedBy?: string | null;\n reviewedAt?: string | null;\n reviewNote?: string | null;\n applicants: BusinessApplicant[];\n checks: BusinessCheck[];\n}\n\nexport interface VerifiedBusinessList {\n items: VerifiedBusinessSummary[];\n totalCount: number;\n page: number;\n pageSize: number;\n}\n\nexport interface BusinessReviewQueueItem {\n applicant: BusinessApplicant;\n business: VerifiedBusinessSummary;\n}\n\nexport interface BusinessReviewQueue {\n items: BusinessReviewQueueItem[];\n totalCount: number;\n page: number;\n pageSize: number;\n}\n\nexport type ReviewDecision = 'approve' | 'reject';\n\nexport const STATUS_LABELS: Record<BusinessVerificationStatus, string> = {\n pending: 'Pending',\n in_review: 'In review',\n verified: 'Verified',\n rejected: 'Rejected',\n failed: 'Failed',\n};\n\nexport const STATUS_CLASSES: Record<BusinessVerificationStatus, string> = {\n pending: 'bg-blue-100 text-blue-800',\n in_review: 'bg-amber-100 text-amber-800',\n verified: 'bg-green-100 text-green-800',\n rejected: 'bg-red-100 text-red-800',\n failed: 'bg-gray-200 text-gray-700',\n};\n\nexport const REASON_LABELS: Record<string, string> = {\n not_found: 'Not found in the registry',\n inactive: 'Not trading',\n name_mismatch: 'Business name mismatch',\n director_mismatch: 'Not a listed director',\n provider_unavailable: 'Provider unavailable',\n awaiting_provider: 'Waiting on the provider',\n awaiting_review: 'Waiting on a reviewer',\n documents_required: 'Documents requested',\n rejected_by_provider: 'Rejected by the provider',\n rejected_by_reviewer: 'Rejected by a reviewer',\n abandoned: 'Applicant abandoned the provider flow',\n};\n\nexport const RELATIONSHIP_LABELS: Record<string, string> = {\n registry_director_match: 'Director on the registry',\n authorized_representative_declared: 'Declared representative',\n provider_verified: 'Verified by the provider',\n};\n","import { Component, OnInit, computed, inject, signal } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { Router } from '@angular/router';\nimport { LocaleDatePipe, PaginationComponent, ToastService, extractErrorMessage } from '@syndew/oluso-ui-core';\nimport { VerificationService } from '../../services/verification.service';\nimport {\n BusinessVerificationStatus,\n REASON_LABELS,\n STATUS_CLASSES,\n STATUS_LABELS,\n VerifiedBusinessSummary,\n} from '../../models/verification.model';\n\n@Component({\n selector: 'verification-businesses-page',\n standalone: true,\n imports: [CommonModule, FormsModule, LocaleDatePipe, PaginationComponent],\n template: `\n <div class=\"space-y-6\">\n <div class=\"flex flex-wrap items-end justify-between gap-3\">\n <div>\n <h1 class=\"text-2xl font-semibold text-gray-900\">Verified businesses</h1>\n <p class=\"text-sm text-gray-500\">Businesses checked by the Business Verification journey step, one per registration number, with the people acting for them.</p>\n </div>\n <div class=\"flex flex-wrap items-center gap-2\">\n <input type=\"search\" [(ngModel)]=\"search\" (keyup.enter)=\"reload()\" name=\"search\" placeholder=\"Name or registration no.\" class=\"w-56\" aria-label=\"Search businesses\" />\n <label for=\"status-filter\" class=\"text-sm text-gray-600\">Status</label>\n <select id=\"status-filter\" [(ngModel)]=\"status\" (ngModelChange)=\"reload()\" name=\"status\">\n <option value=\"\">All</option>\n @for (s of statuses; track s) {\n <option [value]=\"s\">{{ statusLabel(s) }}</option>\n }\n </select>\n </div>\n </div>\n\n @if (loading()) {\n <div class=\"flex items-center justify-center py-12\">\n <div class=\"animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600\"></div>\n </div>\n } @else if (items().length === 0) {\n <p class=\"text-sm text-gray-500 py-12 text-center\">No businesses{{ status || search ? ' match' : ' yet' }}.</p>\n } @else {\n <div class=\"overflow-x-auto rounded-lg border border-gray-200\">\n <table class=\"min-w-full divide-y divide-gray-200 text-sm\">\n <thead class=\"bg-gray-50 text-xs uppercase text-gray-500\">\n <tr>\n <th class=\"px-4 py-2 text-left\">Business</th>\n <th class=\"px-4 py-2 text-left\">Registration no.</th>\n <th class=\"px-4 py-2 text-left\">Country</th>\n <th class=\"px-4 py-2 text-left\">Applicants</th>\n <th class=\"px-4 py-2 text-left\">Registry checked</th>\n <th class=\"px-4 py-2 text-left\">Status</th>\n <th class=\"px-4 py-2 text-left\">Updated</th>\n </tr>\n </thead>\n <tbody class=\"divide-y divide-gray-100 bg-white\">\n @for (b of items(); track b.id) {\n <tr class=\"cursor-pointer hover:bg-gray-50\" (click)=\"open(b)\">\n <td class=\"px-4 py-2 font-medium text-gray-900\">{{ b.registeredName || '—' }}</td>\n <td class=\"px-4 py-2 font-mono text-xs\">{{ b.registrationNumber }}</td>\n <td class=\"px-4 py-2\">{{ b.country }}</td>\n <td class=\"px-4 py-2\">{{ b.applicantCount }}</td>\n <td class=\"px-4 py-2 text-gray-600\">{{ b.registryCheckedAt ? (b.registryCheckedAt | localeDate: 'medium') : '—' }}</td>\n <td class=\"px-4 py-2\">\n <span class=\"px-2 py-0.5 rounded-full text-xs font-medium\" [ngClass]=\"badge(b.status)\">{{ statusLabel(b.status) }}</span>\n @if (b.statusReason) { <span class=\"ml-1 text-xs text-gray-500\">{{ reason(b.statusReason) }}</span> }\n </td>\n <td class=\"px-4 py-2 text-gray-600\">{{ b.updatedAt | localeDate: 'medium' }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n <ui-pagination [page]=\"page()\" [pageSize]=\"pageSize()\" [totalCount]=\"total()\" [totalPages]=\"totalPages()\"\n (pageChange)=\"goTo($event)\" (pageSizeChange)=\"resize($event)\" />\n }\n </div>\n `,\n})\nexport class BusinessesPageComponent implements OnInit {\n private readonly service = inject(VerificationService);\n private readonly toast = inject(ToastService);\n private readonly router = inject(Router);\n\n readonly statuses = Object.keys(STATUS_LABELS) as BusinessVerificationStatus[];\n readonly loading = signal(true);\n readonly items = signal<VerifiedBusinessSummary[]>([]);\n readonly total = signal(0);\n readonly page = signal(1);\n readonly pageSize = signal(25);\n readonly totalPages = computed(() => Math.max(1, Math.ceil(this.total() / this.pageSize())));\n\n status = '';\n search = '';\n\n ngOnInit(): void {\n this.load();\n }\n\n statusLabel(status: BusinessVerificationStatus): string {\n return STATUS_LABELS[status] ?? status;\n }\n\n badge(status: BusinessVerificationStatus): string {\n return STATUS_CLASSES[status] ?? 'bg-gray-100 text-gray-700';\n }\n\n reason(reason: string): string {\n return REASON_LABELS[reason] ?? reason;\n }\n\n reload(): void {\n this.page.set(1);\n this.load();\n }\n\n goTo(page: number): void {\n this.page.set(page);\n this.load();\n }\n\n resize(size: number): void {\n this.pageSize.set(size);\n this.reload();\n }\n\n open(business: VerifiedBusinessSummary): void {\n this.router.navigate(['/verification/businesses', business.id]);\n }\n\n private load(): void {\n this.loading.set(true);\n this.service.listBusinesses(this.status, this.search.trim(), this.page(), this.pageSize()).subscribe({\n next: (list) => {\n this.items.set(list.items);\n this.total.set(list.totalCount);\n this.loading.set(false);\n },\n error: (err) => {\n this.loading.set(false);\n this.toast.error(extractErrorMessage(err, 'Failed to load businesses'));\n },\n });\n }\n}\n","import { Component, OnInit, computed, inject, signal } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { HttpErrorResponse } from '@angular/common/http';\nimport { ActivatedRoute, RouterLink } from '@angular/router';\nimport {\n ButtonComponent,\n LocaleDatePipe,\n PermissionsService,\n PromptDialogComponent,\n ToastService,\n extractErrorMessage,\n openFetchedFile,\n} from '@syndew/oluso-ui-core';\nimport { VerificationService } from '../../services/verification.service';\nimport {\n BusinessApplicant,\n BusinessCheck,\n BusinessDocument,\n BusinessVerificationStatus,\n REASON_LABELS,\n RELATIONSHIP_LABELS,\n ReviewDecision,\n STATUS_CLASSES,\n STATUS_LABELS,\n VerifiedBusinessDetail,\n} from '../../models/verification.model';\n\ninterface PendingReview {\n target: 'business' | 'applicant';\n id: string;\n decision: ReviewDecision;\n}\n\nconst CHECK_KIND_LABELS: Record<BusinessCheck['kind'], string> = {\n lookup: 'Registry lookup',\n reuse: 'Reused registry result',\n hosted_session: 'Hosted verification',\n review: 'Review',\n};\n\n@Component({\n selector: 'verification-business-detail-page',\n standalone: true,\n imports: [CommonModule, RouterLink, ButtonComponent, LocaleDatePipe, PromptDialogComponent],\n template: `\n <div class=\"space-y-6\">\n <a routerLink=\"/verification/businesses\" class=\"text-sm text-primary-700 hover:underline\">← Verified businesses</a>\n\n @if (loading()) {\n <div class=\"flex items-center justify-center py-12\">\n <div class=\"animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600\"></div>\n </div>\n } @else if (business(); as b) {\n <div class=\"flex flex-wrap items-start justify-between gap-3\">\n <div>\n <h1 class=\"text-2xl font-semibold text-gray-900\">{{ b.registeredName || b.registrationNumber }}</h1>\n <p class=\"text-sm text-gray-500 font-mono\">{{ b.country }} · {{ b.registrationNumber }}</p>\n </div>\n <div class=\"flex items-center gap-2\">\n <span class=\"px-2 py-0.5 rounded-full text-xs font-medium\" [ngClass]=\"badge(b.status)\">{{ statusLabel(b.status) }}</span>\n @if (b.statusReason) { <span class=\"text-xs text-gray-500\">{{ reason(b.statusReason) }}</span> }\n </div>\n </div>\n\n @if (b.statusMessage) {\n <div class=\"bg-gray-50 border border-gray-200 rounded p-3 text-xs text-gray-700\">{{ b.statusMessage }}</div>\n }\n\n @if (canReview() && (b.status === 'pending' || b.status === 'in_review')) {\n <div class=\"rounded-lg border border-amber-200 bg-amber-50 p-4 flex flex-wrap items-center justify-between gap-3\">\n <p class=\"text-sm text-amber-900\">This business has no registry decision yet. You can decide it yourself; people waiting on it follow your decision.</p>\n <div class=\"flex gap-2\">\n <ui-button size=\"sm\" (click)=\"ask('business', b.id, 'approve')\">Verify business</ui-button>\n <ui-button size=\"sm\" variant=\"danger\" (click)=\"ask('business', b.id, 'reject')\">Reject business</ui-button>\n </div>\n </div>\n }\n\n <section class=\"rounded-lg border border-gray-200 p-4\">\n <h2 class=\"text-sm font-semibold text-gray-900 mb-3\">Registry record</h2>\n <dl class=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-x-6 gap-y-3 text-sm\">\n <div><dt class=\"text-xs text-gray-500\">Registered name</dt><dd>{{ b.registeredName || '—' }}</dd></div>\n <div><dt class=\"text-xs text-gray-500\">Registration type</dt><dd>{{ b.registrationType || '—' }}</dd></div>\n <div><dt class=\"text-xs text-gray-500\">Entity type</dt><dd>{{ b.entityType || '—' }}</dd></div>\n <div><dt class=\"text-xs text-gray-500\">Registered on</dt><dd>{{ b.registrationDate || '—' }}</dd></div>\n <div><dt class=\"text-xs text-gray-500\">Registry status</dt><dd>{{ b.rawStatus || 'Not reported' }} ({{ b.activityStatus }})</dd></div>\n <div><dt class=\"text-xs text-gray-500\">Provider</dt><dd>{{ b.registryProviderId || '—' }}{{ b.registryEnvironment ? ' · ' + b.registryEnvironment : '' }}</dd></div>\n <div class=\"sm:col-span-2 lg:col-span-3\"><dt class=\"text-xs text-gray-500\">Address</dt><dd>{{ b.registeredAddress || '—' }}</dd></div>\n <div><dt class=\"text-xs text-gray-500\">Snapshot taken</dt><dd>{{ b.registryCheckedAt ? (b.registryCheckedAt | localeDate: 'medium') : 'No snapshot yet' }}</dd></div>\n <div><dt class=\"text-xs text-gray-500\">Decided</dt><dd>{{ b.decidedAt ? (b.decidedAt | localeDate: 'medium') : '—' }}</dd></div>\n @if (b.reviewedAt) {\n <div><dt class=\"text-xs text-gray-500\">Reviewed</dt><dd>{{ b.reviewedAt | localeDate: 'medium' }}{{ b.reviewNote ? ' — ' + b.reviewNote : '' }}</dd></div>\n }\n </dl>\n <div class=\"mt-4\">\n <p class=\"text-xs font-medium text-gray-500 mb-1\">Directors and other listed people</p>\n @if (b.people.length === 0) {\n <p class=\"text-xs text-gray-500\">None returned.</p>\n } @else {\n <ul class=\"text-sm divide-y divide-gray-100 border border-gray-200 rounded\">\n @for (p of b.people; track $index) {\n <li class=\"px-3 py-1.5 flex justify-between gap-3\"><span>{{ p.name }}</span><span class=\"text-gray-500\">{{ p.role || '—' }}</span></li>\n }\n </ul>\n }\n </div>\n </section>\n\n <section class=\"rounded-lg border border-gray-200 p-4 space-y-4\">\n <h2 class=\"text-sm font-semibold text-gray-900\">People acting for this business</h2>\n @for (a of b.applicants; track a.id) {\n <div class=\"rounded-md border border-gray-100 p-3 space-y-2\">\n <div class=\"flex flex-wrap items-center justify-between gap-2\">\n <div>\n <p class=\"text-sm font-medium text-gray-900\">{{ a.subjectName || a.subjectId || 'Before sign-up' }}</p>\n <p class=\"text-xs text-gray-500\">{{ relationship(a.relationship) }} · evidence required: {{ a.evidenceRequirement }}</p>\n </div>\n <div class=\"flex items-center gap-2\">\n <span class=\"px-2 py-0.5 rounded-full text-xs font-medium\" [ngClass]=\"badge(a.status)\">{{ statusLabel(a.status) }}</span>\n @if (a.statusReason) { <span class=\"text-xs text-gray-500\">{{ reason(a.statusReason) }}</span> }\n </div>\n </div>\n <dl class=\"grid grid-cols-1 sm:grid-cols-3 gap-x-6 gap-y-2 text-xs\">\n <div><dt class=\"text-gray-500\">Name entered</dt><dd class=\"text-gray-900\">{{ a.providedBusinessName || '—' }}</dd></div>\n <div><dt class=\"text-gray-500\">Name match</dt><dd class=\"text-gray-900\">{{ match(a.nameMatched) }}</dd></div>\n <div><dt class=\"text-gray-500\">Director match</dt><dd class=\"text-gray-900\">{{ match(a.directorMatched) }}{{ a.matchedPersonName ? ' (' + a.matchedPersonName + (a.matchedPersonRole ? ', ' + a.matchedPersonRole : '') + ')' : '' }}</dd></div>\n <div><dt class=\"text-gray-500\">Submitted</dt><dd class=\"text-gray-900\">{{ a.createdAt | localeDate: 'medium' }}</dd></div>\n <div><dt class=\"text-gray-500\">Decided</dt><dd class=\"text-gray-900\">{{ a.decidedAt ? (a.decidedAt | localeDate: 'medium') : '—' }}</dd></div>\n <div><dt class=\"text-gray-500\">Outcome email</dt><dd class=\"text-gray-900\">{{ a.outcomeEmailRequested ? 'Requested' : '—' }}</dd></div>\n @if (a.reviewedAt) {\n <div class=\"sm:col-span-3\"><dt class=\"text-gray-500\">Reviewed</dt><dd class=\"text-gray-900\">{{ a.reviewedAt | localeDate: 'medium' }}{{ a.reviewNote ? ' — ' + a.reviewNote : '' }}</dd></div>\n }\n </dl>\n @if (a.documents.length > 0) {\n <div>\n <p class=\"text-xs font-medium text-gray-500 mb-1\">Documents</p>\n <ul class=\"space-y-1\">\n @for (d of a.documents; track d.id) {\n <li class=\"flex items-center justify-between gap-2 text-sm\">\n <span class=\"truncate\">{{ d.fileName }} <span class=\"text-xs text-gray-500\">{{ size(d.sizeBytes) }} · {{ d.createdAt | localeDate: 'medium' }}</span></span>\n <ui-button size=\"xs\" variant=\"secondary\" (click)=\"openDocument(a, d)\" [disabled]=\"downloading() === d.id\">Open</ui-button>\n </li>\n }\n </ul>\n </div>\n }\n @if (canReview() && (a.status === 'pending' || a.status === 'in_review')) {\n <div class=\"flex flex-wrap gap-2 pt-1\">\n @if (b.status === 'verified') {\n <ui-button size=\"sm\" (click)=\"ask('applicant', a.id, 'approve')\">Approve applicant</ui-button>\n }\n <ui-button size=\"sm\" variant=\"danger\" (click)=\"ask('applicant', a.id, 'reject')\">Reject applicant</ui-button>\n @if (a.statusReason === 'awaiting_provider') {\n <ui-button size=\"sm\" variant=\"secondary\" (click)=\"refresh(a)\" [disabled]=\"refreshing() === a.id\">Ask the provider again</ui-button>\n }\n </div>\n }\n </div>\n } @empty {\n <p class=\"text-xs text-gray-500\">Nobody yet.</p>\n }\n </section>\n\n <section class=\"rounded-lg border border-gray-200 p-4\">\n <h2 class=\"text-sm font-semibold text-gray-900 mb-3\">Checks</h2>\n <div class=\"overflow-x-auto\">\n <table class=\"min-w-full divide-y divide-gray-200 text-xs\">\n <thead class=\"bg-gray-50 uppercase text-gray-500\">\n <tr>\n <th class=\"px-3 py-2 text-left\">When</th>\n <th class=\"px-3 py-2 text-left\">Check</th>\n <th class=\"px-3 py-2 text-left\">Outcome</th>\n <th class=\"px-3 py-2 text-left\">Provider</th>\n <th class=\"px-3 py-2 text-left\">Reference</th>\n <th class=\"px-3 py-2 text-left\">By</th>\n <th class=\"px-3 py-2 text-left\">Detail</th>\n </tr>\n </thead>\n <tbody class=\"divide-y divide-gray-100\">\n @for (c of b.checks; track c.id) {\n <tr>\n <td class=\"px-3 py-2 whitespace-nowrap\">{{ c.createdAt | localeDate: 'medium' }}</td>\n <td class=\"px-3 py-2\">{{ kind(c.kind) }}</td>\n <td class=\"px-3 py-2\">{{ c.outcome }}</td>\n <td class=\"px-3 py-2\">{{ c.providerId || '—' }}{{ c.environment ? ' · ' + c.environment : '' }}</td>\n <td class=\"px-3 py-2 font-mono break-all\">{{ c.sessionReference || c.providerReference || '—' }}</td>\n <td class=\"px-3 py-2\">{{ c.originKind || '—' }}{{ c.originName ? ': ' + c.originName : '' }}</td>\n <td class=\"px-3 py-2 text-gray-600\">{{ c.message || '' }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </section>\n }\n </div>\n\n <ui-prompt-dialog\n [open]=\"!!pending()\"\n [title]=\"dialogTitle()\"\n [message]=\"dialogMessage()\"\n label=\"Note (optional)\"\n inputType=\"textarea\"\n [maxLength]=\"1000\"\n [confirmText]=\"pending()?.decision === 'approve' ? 'Approve' : 'Reject'\"\n [loading]=\"reviewing()\"\n (confirmed)=\"review($event)\"\n (cancelled)=\"pending.set(null)\" />\n `,\n})\nexport class BusinessDetailPageComponent implements OnInit {\n private readonly service = inject(VerificationService);\n private readonly toast = inject(ToastService);\n private readonly permissions = inject(PermissionsService);\n private readonly route = inject(ActivatedRoute);\n\n readonly loading = signal(true);\n readonly business = signal<VerifiedBusinessDetail | null>(null);\n readonly pending = signal<PendingReview | null>(null);\n readonly reviewing = signal(false);\n readonly refreshing = signal<string | null>(null);\n readonly downloading = signal<string | null>(null);\n readonly canReview = computed(() => this.permissions.hasPermission('business_verification.review'));\n\n readonly dialogTitle = computed(() => {\n const p = this.pending();\n if (!p) return '';\n const what = p.target === 'business' ? 'business' : 'applicant';\n return p.decision === 'approve' ? `Approve ${what}` : `Reject ${what}`;\n });\n\n readonly dialogMessage = computed(() => {\n const p = this.pending();\n if (!p) return '';\n if (p.target === 'business') {\n return p.decision === 'approve'\n ? 'Mark the business as verified. People waiting on it are verified when the step asked for nothing more, otherwise they move to review.'\n : 'Reject the business. Everyone waiting on it is rejected; webhooks and workflows receive the decision.';\n }\n return p.decision === 'approve'\n ? 'Let this person act for the business. Webhooks and workflows receive the decision and the applicant is emailed if they asked.'\n : 'Refuse this person. Webhooks and workflows receive the decision and the applicant is emailed if they asked.';\n });\n\n ngOnInit(): void {\n this.load();\n }\n\n statusLabel(status: BusinessVerificationStatus): string {\n return STATUS_LABELS[status] ?? status;\n }\n\n badge(status: BusinessVerificationStatus): string {\n return STATUS_CLASSES[status] ?? 'bg-gray-100 text-gray-700';\n }\n\n reason(reason: string): string {\n return REASON_LABELS[reason] ?? reason;\n }\n\n relationship(relationship: string): string {\n return RELATIONSHIP_LABELS[relationship] ?? relationship;\n }\n\n kind(kind: BusinessCheck['kind']): string {\n return CHECK_KIND_LABELS[kind] ?? kind;\n }\n\n match(value: boolean | null | undefined): string {\n if (value === true) return 'Matched';\n if (value === false) return 'Did not match';\n return 'Not checked';\n }\n\n size(bytes: number): string {\n return bytes >= 1024 * 1024 ? `${(bytes / (1024 * 1024)).toFixed(1)} MB` : `${Math.max(1, Math.round(bytes / 1024))} KB`;\n }\n\n ask(target: 'business' | 'applicant', id: string, decision: ReviewDecision): void {\n this.pending.set({ target, id, decision });\n }\n\n review(note: string): void {\n const p = this.pending();\n if (!p) return;\n this.reviewing.set(true);\n const done = (message: string) => {\n this.reviewing.set(false);\n this.pending.set(null);\n this.toast.success(message);\n this.load();\n };\n const failed = (err: unknown) => {\n this.reviewing.set(false);\n this.pending.set(null);\n this.toast.error(extractErrorMessage(err, 'Failed to record the review'));\n if (err instanceof HttpErrorResponse && err.status === 409) this.load();\n };\n const text = note.trim() || undefined;\n if (p.target === 'business') {\n this.service.reviewBusiness(p.id, p.decision, text).subscribe({\n next: () => done(p.decision === 'approve' ? 'Business verified' : 'Business rejected'),\n error: failed,\n });\n } else {\n this.service.reviewApplicant(p.id, p.decision, text).subscribe({\n next: () => done(p.decision === 'approve' ? 'Applicant approved' : 'Applicant rejected'),\n error: failed,\n });\n }\n }\n\n refresh(applicant: BusinessApplicant): void {\n this.refreshing.set(applicant.id);\n this.service.refreshApplicant(applicant.id).subscribe({\n next: (updated) => {\n this.refreshing.set(null);\n this.toast.success(updated.status === applicant.status ? 'The provider has no decision yet' : 'Updated from the provider');\n this.load();\n },\n error: (err) => {\n this.refreshing.set(null);\n this.toast.error(extractErrorMessage(err, 'Failed to ask the provider'));\n },\n });\n }\n\n openDocument(applicant: BusinessApplicant, document: BusinessDocument): void {\n this.downloading.set(document.id);\n this.service.downloadDocument(applicant.id, document.id).subscribe({\n next: (blob) => {\n this.downloading.set(null);\n openFetchedFile(blob, document.fileName);\n },\n error: (err) => {\n this.downloading.set(null);\n this.toast.error(extractErrorMessage(err, 'Failed to open the document'));\n },\n });\n }\n\n private load(): void {\n const id = this.route.snapshot.paramMap.get('id');\n if (!id) return;\n this.loading.set(this.business() === null);\n this.service.getBusiness(id).subscribe({\n next: (business) => {\n this.business.set(business);\n this.loading.set(false);\n },\n error: (err) => {\n this.loading.set(false);\n this.toast.error(extractErrorMessage(err, 'Failed to load the business'));\n },\n });\n }\n}\n","import { Component, OnInit, computed, inject, signal } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { RouterLink } from '@angular/router';\nimport { LocaleDatePipe, PaginationComponent, ToastService, extractErrorMessage } from '@syndew/oluso-ui-core';\nimport { VerificationService } from '../../services/verification.service';\nimport {\n BusinessReviewQueueItem,\n BusinessVerificationStatus,\n REASON_LABELS,\n RELATIONSHIP_LABELS,\n STATUS_CLASSES,\n STATUS_LABELS,\n} from '../../models/verification.model';\n\n@Component({\n selector: 'verification-review-queue-page',\n standalone: true,\n imports: [CommonModule, RouterLink, LocaleDatePipe, PaginationComponent],\n template: `\n <div class=\"space-y-6\">\n <div>\n <h1 class=\"text-2xl font-semibold text-gray-900\">Verification review queue</h1>\n <p class=\"text-sm text-gray-500\">People waiting on a decision to act for a business: documents to check, a provider that couldn't answer, or a hosted check still open. Oldest first.</p>\n </div>\n\n @if (loading()) {\n <div class=\"flex items-center justify-center py-12\">\n <div class=\"animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600\"></div>\n </div>\n } @else if (items().length === 0) {\n <p class=\"text-sm text-gray-500 py-12 text-center\">Nobody is waiting on a decision.</p>\n } @else {\n <div class=\"overflow-x-auto rounded-lg border border-gray-200\">\n <table class=\"min-w-full divide-y divide-gray-200 text-sm\">\n <thead class=\"bg-gray-50 text-xs uppercase text-gray-500\">\n <tr>\n <th class=\"px-4 py-2 text-left\">Waiting since</th>\n <th class=\"px-4 py-2 text-left\">Business</th>\n <th class=\"px-4 py-2 text-left\">Applicant</th>\n <th class=\"px-4 py-2 text-left\">Grounds</th>\n <th class=\"px-4 py-2 text-left\">Documents</th>\n <th class=\"px-4 py-2 text-left\">Status</th>\n </tr>\n </thead>\n <tbody class=\"divide-y divide-gray-100 bg-white\">\n @for (item of items(); track item.applicant.id) {\n <tr>\n <td class=\"px-4 py-2 text-gray-600\">{{ item.applicant.updatedAt | localeDate: 'medium' }}</td>\n <td class=\"px-4 py-2\">\n <a class=\"font-medium text-primary-700 hover:underline\" [routerLink]=\"['/verification/businesses', item.business.id]\">{{ item.business.registeredName || item.business.registrationNumber }}</a>\n <div class=\"text-xs text-gray-500 font-mono\">{{ item.business.country }} · {{ item.business.registrationNumber }}</div>\n </td>\n <td class=\"px-4 py-2\">{{ item.applicant.subjectName || (item.applicant.subjectId ? item.applicant.subjectId : 'Before sign-up') }}</td>\n <td class=\"px-4 py-2\">{{ relationship(item.applicant.relationship) }}</td>\n <td class=\"px-4 py-2\">{{ item.applicant.documents.length }}</td>\n <td class=\"px-4 py-2\">\n <span class=\"px-2 py-0.5 rounded-full text-xs font-medium\" [ngClass]=\"badge(item.applicant.status)\">{{ statusLabel(item.applicant.status) }}</span>\n @if (item.applicant.statusReason) { <span class=\"ml-1 text-xs text-gray-500\">{{ reason(item.applicant.statusReason) }}</span> }\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n <ui-pagination [page]=\"page()\" [pageSize]=\"pageSize()\" [totalCount]=\"total()\" [totalPages]=\"totalPages()\"\n (pageChange)=\"goTo($event)\" (pageSizeChange)=\"resize($event)\" />\n }\n </div>\n `,\n})\nexport class ReviewQueuePageComponent implements OnInit {\n private readonly service = inject(VerificationService);\n private readonly toast = inject(ToastService);\n\n readonly loading = signal(true);\n readonly items = signal<BusinessReviewQueueItem[]>([]);\n readonly total = signal(0);\n readonly page = signal(1);\n readonly pageSize = signal(25);\n readonly totalPages = computed(() => Math.max(1, Math.ceil(this.total() / this.pageSize())));\n\n ngOnInit(): void {\n this.load();\n }\n\n statusLabel(status: BusinessVerificationStatus): string {\n return STATUS_LABELS[status] ?? status;\n }\n\n badge(status: BusinessVerificationStatus): string {\n return STATUS_CLASSES[status] ?? 'bg-gray-100 text-gray-700';\n }\n\n reason(reason: string): string {\n return REASON_LABELS[reason] ?? reason;\n }\n\n relationship(relationship: string): string {\n return RELATIONSHIP_LABELS[relationship] ?? relationship;\n }\n\n goTo(page: number): void {\n this.page.set(page);\n this.load();\n }\n\n resize(size: number): void {\n this.pageSize.set(size);\n this.page.set(1);\n this.load();\n }\n\n private load(): void {\n this.loading.set(true);\n this.service.reviewQueue(this.page(), this.pageSize()).subscribe({\n next: (queue) => {\n this.items.set(queue.items);\n this.total.set(queue.totalCount);\n this.loading.set(false);\n },\n error: (err) => {\n this.loading.set(false);\n this.toast.error(extractErrorMessage(err, 'Failed to load the review queue'));\n },\n });\n }\n}\n","// Business (KYB) verification plugin\nexport { VERIFICATION_PLUGIN } from './lib/verification.plugin';\nexport { VERIFICATION_ROUTES } from './lib/verification.routes';\n\n// Models\nexport * from './lib/models/verification.model';\n\n// Services\nexport { VerificationService } from './lib/services/verification.service';\n\n// Components\nexport { VerificationSettingsTabComponent } from './lib/components/verification-settings-tab.component';\nexport { BusinessesPageComponent } from './lib/pages/businesses/businesses-page.component';\nexport { BusinessDetailPageComponent } from './lib/pages/business-detail/business-detail-page.component';\nexport { ReviewQueuePageComponent } from './lib/pages/review-queue/review-queue-page.component';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["i1","i2"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,35 @@
1
1
  {
2
2
  "name": "@syndew/oluso-plugin-verification",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.0.1",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "git+https://github.com/SyndewTech/Oluso.git",
7
+ "directory": "src/frontend/libs/plugins/verification"
8
+ },
9
+ "peerDependencies": {
10
+ "@angular/common": "^21.1.0",
11
+ "@angular/core": "^21.1.0",
12
+ "@syndew/oluso-admin-ui": "*",
13
+ "@syndew/oluso-ui-core": "*"
14
+ },
15
+ "sideEffects": false,
16
+ "publishConfig": {
17
+ "access": "public",
18
+ "registry": "https://registry.npmjs.org/"
19
+ },
20
+ "module": "fesm2022/syndew-oluso-plugin-verification.mjs",
21
+ "typings": "types/syndew-oluso-plugin-verification.d.ts",
22
+ "exports": {
23
+ "./package.json": {
24
+ "default": "./package.json"
25
+ },
26
+ ".": {
27
+ "types": "./types/syndew-oluso-plugin-verification.d.ts",
28
+ "default": "./fesm2022/syndew-oluso-plugin-verification.mjs"
29
+ }
30
+ },
31
+ "type": "module",
32
+ "dependencies": {
33
+ "tslib": "^2.3.0"
34
+ }
6
35
  }
@@ -0,0 +1,321 @@
1
+ import { AdminPlugin } from '@syndew/oluso-ui-core';
2
+ import { Routes } from '@angular/router';
3
+ import { Observable } from 'rxjs';
4
+ import * as _angular_core from '@angular/core';
5
+ import { OnInit } from '@angular/core';
6
+
7
+ /**
8
+ * Business (KYB) verification admin plugin: verified businesses with the people acting
9
+ * for them and their documents, the review queue, and the provider settings tab.
10
+ */
11
+ declare const VERIFICATION_PLUGIN: AdminPlugin;
12
+
13
+ declare const VERIFICATION_ROUTES: Routes;
14
+
15
+ /** A verification vendor the platform supports (api/admin/business-verification/providers). */
16
+ interface VerificationProviderInfo {
17
+ id: string;
18
+ displayName: string;
19
+ businessCountries: string[];
20
+ /** Countries its hosted (provider-run) business verification covers. */
21
+ hostedCountries: string[];
22
+ /** The URL to give the provider for webhooks, on this organisation's host. */
23
+ webhookUrl?: string | null;
24
+ credentialFields: VerificationCredentialField[];
25
+ options: VerificationProviderOption[];
26
+ }
27
+ interface VerificationCredentialField {
28
+ name: string;
29
+ label: string;
30
+ secret: boolean;
31
+ description?: string | null;
32
+ /** Needed to enable the provider; the others serve hosted verification only. */
33
+ required: boolean;
34
+ }
35
+ interface VerificationProviderOption {
36
+ name: string;
37
+ label: string;
38
+ choices: string[];
39
+ default: string;
40
+ description?: string | null;
41
+ }
42
+ interface BusinessVerificationCountry {
43
+ code: string;
44
+ name: string;
45
+ providerIds: string[];
46
+ routedProviderId?: string | null;
47
+ providerName: string;
48
+ }
49
+ type VerificationEnvironment = 'Sandbox' | 'Live';
50
+ /** Secrets are never returned; storedSecrets names the ones that are set. */
51
+ interface ConfiguredVerificationProvider {
52
+ providerId: string;
53
+ enabled: boolean;
54
+ environment: VerificationEnvironment;
55
+ credentials: Record<string, string>;
56
+ storedSecrets: string[];
57
+ options: Record<string, string>;
58
+ }
59
+ interface IdentityVerificationSettings {
60
+ providers: ConfiguredVerificationProvider[];
61
+ businessRouting: Record<string, string>;
62
+ businessReuseMaxAgeDays: number;
63
+ }
64
+ interface UpdateVerificationProviderRequest {
65
+ providerId: string;
66
+ enabled: boolean;
67
+ environment: VerificationEnvironment;
68
+ /** A blank secret keeps the stored value. */
69
+ credentials: Record<string, string>;
70
+ options: Record<string, string>;
71
+ }
72
+ interface UpdateIdentityVerificationSettingsRequest {
73
+ providers: UpdateVerificationProviderRequest[];
74
+ businessRouting: Record<string, string>;
75
+ businessReuseMaxAgeDays: number;
76
+ }
77
+ interface VerificationConnectionTestResult {
78
+ success: boolean;
79
+ message: string;
80
+ }
81
+ type BusinessVerificationStatus = 'pending' | 'in_review' | 'verified' | 'rejected' | 'failed';
82
+ interface VerifiedBusinessSummary {
83
+ id: string;
84
+ status: BusinessVerificationStatus;
85
+ statusReason?: string | null;
86
+ country: string;
87
+ registrationNumber: string;
88
+ registeredName?: string | null;
89
+ registryProviderId?: string | null;
90
+ registryCheckedAt?: string | null;
91
+ applicantCount: number;
92
+ createdAt: string;
93
+ updatedAt: string;
94
+ }
95
+ interface BusinessPerson {
96
+ name: string;
97
+ role?: string | null;
98
+ }
99
+ interface BusinessDocument {
100
+ id: string;
101
+ fileName: string;
102
+ contentType: string;
103
+ sizeBytes: number;
104
+ createdAt: string;
105
+ }
106
+ interface BusinessApplicant {
107
+ id: string;
108
+ businessId: string;
109
+ subjectId?: string | null;
110
+ subjectName?: string | null;
111
+ relationship: string;
112
+ evidenceRequirement: string;
113
+ status: BusinessVerificationStatus;
114
+ statusReason?: string | null;
115
+ statusMessage?: string | null;
116
+ providedBusinessName?: string | null;
117
+ nameMatched?: boolean | null;
118
+ directorMatched?: boolean | null;
119
+ matchedPersonName?: string | null;
120
+ matchedPersonRole?: string | null;
121
+ documents: BusinessDocument[];
122
+ outcomeEmailRequested: boolean;
123
+ createdAt: string;
124
+ updatedAt: string;
125
+ decidedAt?: string | null;
126
+ reviewedBy?: string | null;
127
+ reviewedAt?: string | null;
128
+ reviewNote?: string | null;
129
+ }
130
+ interface BusinessCheck {
131
+ id: string;
132
+ applicantId?: string | null;
133
+ kind: 'lookup' | 'reuse' | 'hosted_session' | 'review';
134
+ outcome: string;
135
+ providerId?: string | null;
136
+ environment?: string | null;
137
+ providerReference?: string | null;
138
+ sessionReference?: string | null;
139
+ message?: string | null;
140
+ reusedFromCheckId?: string | null;
141
+ originKind?: string | null;
142
+ originId?: string | null;
143
+ originName?: string | null;
144
+ createdAt: string;
145
+ completedAt?: string | null;
146
+ }
147
+ interface VerifiedBusinessDetail extends VerifiedBusinessSummary {
148
+ registrationType?: string | null;
149
+ statusMessage?: string | null;
150
+ entityType?: string | null;
151
+ registrationDate?: string | null;
152
+ registeredAddress?: string | null;
153
+ activityStatus: 'Unknown' | 'Active' | 'Inactive';
154
+ rawStatus?: string | null;
155
+ registryEnvironment?: string | null;
156
+ people: BusinessPerson[];
157
+ decidedAt?: string | null;
158
+ reviewedBy?: string | null;
159
+ reviewedAt?: string | null;
160
+ reviewNote?: string | null;
161
+ applicants: BusinessApplicant[];
162
+ checks: BusinessCheck[];
163
+ }
164
+ interface VerifiedBusinessList {
165
+ items: VerifiedBusinessSummary[];
166
+ totalCount: number;
167
+ page: number;
168
+ pageSize: number;
169
+ }
170
+ interface BusinessReviewQueueItem {
171
+ applicant: BusinessApplicant;
172
+ business: VerifiedBusinessSummary;
173
+ }
174
+ interface BusinessReviewQueue {
175
+ items: BusinessReviewQueueItem[];
176
+ totalCount: number;
177
+ page: number;
178
+ pageSize: number;
179
+ }
180
+ type ReviewDecision = 'approve' | 'reject';
181
+ declare const STATUS_LABELS: Record<BusinessVerificationStatus, string>;
182
+ declare const STATUS_CLASSES: Record<BusinessVerificationStatus, string>;
183
+ declare const REASON_LABELS: Record<string, string>;
184
+ declare const RELATIONSHIP_LABELS: Record<string, string>;
185
+
186
+ /** Business (KYB) verification: providers, settings, businesses, applicants and reviews (api/admin/business-verification). */
187
+ declare class VerificationService {
188
+ private readonly api;
189
+ private readonly endpoint;
190
+ getProviders(): Observable<VerificationProviderInfo[]>;
191
+ getCountries(): Observable<BusinessVerificationCountry[]>;
192
+ getSettings(): Observable<IdentityVerificationSettings>;
193
+ updateSettings(input: UpdateIdentityVerificationSettingsRequest): Observable<IdentityVerificationSettings>;
194
+ testProvider(providerId: string): Observable<VerificationConnectionTestResult>;
195
+ listBusinesses(status: string, search: string, page: number, pageSize: number): Observable<VerifiedBusinessList>;
196
+ getBusiness(id: string): Observable<VerifiedBusinessDetail>;
197
+ reviewQueue(page: number, pageSize: number): Observable<BusinessReviewQueue>;
198
+ reviewBusiness(id: string, decision: ReviewDecision, note?: string): Observable<VerifiedBusinessDetail>;
199
+ reviewApplicant(id: string, decision: ReviewDecision, note?: string): Observable<BusinessApplicant>;
200
+ refreshApplicant(id: string): Observable<BusinessApplicant>;
201
+ downloadDocument(applicantId: string, documentId: string): Observable<Blob>;
202
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<VerificationService, never>;
203
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<VerificationService>;
204
+ }
205
+
206
+ interface ProviderForm {
207
+ info: VerificationProviderInfo;
208
+ enabled: boolean;
209
+ environment: VerificationEnvironment;
210
+ credentials: Record<string, string>;
211
+ storedSecrets: string[];
212
+ options: Record<string, string>;
213
+ }
214
+ declare class VerificationSettingsTabComponent implements OnInit {
215
+ private readonly service;
216
+ private readonly toast;
217
+ readonly loading: _angular_core.WritableSignal<boolean>;
218
+ readonly saving: _angular_core.WritableSignal<boolean>;
219
+ readonly testing: _angular_core.WritableSignal<string | null>;
220
+ readonly loadError: _angular_core.WritableSignal<string | null>;
221
+ readonly providers: _angular_core.WritableSignal<ProviderForm[]>;
222
+ readonly countries: _angular_core.WritableSignal<BusinessVerificationCountry[]>;
223
+ readonly testResults: _angular_core.WritableSignal<Record<string, VerificationConnectionTestResult>>;
224
+ routing: Record<string, string>;
225
+ reuseDays: number;
226
+ ngOnInit(): void;
227
+ countryNames(codes: string[]): string;
228
+ providerName(id: string): string;
229
+ copy(text: string): void;
230
+ save(): void;
231
+ test(providerId: string): void;
232
+ private apply;
233
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<VerificationSettingsTabComponent, never>;
234
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<VerificationSettingsTabComponent, "verification-settings-tab", never, {}, {}, never, never, true, never>;
235
+ }
236
+
237
+ declare class BusinessesPageComponent implements OnInit {
238
+ private readonly service;
239
+ private readonly toast;
240
+ private readonly router;
241
+ readonly statuses: BusinessVerificationStatus[];
242
+ readonly loading: _angular_core.WritableSignal<boolean>;
243
+ readonly items: _angular_core.WritableSignal<VerifiedBusinessSummary[]>;
244
+ readonly total: _angular_core.WritableSignal<number>;
245
+ readonly page: _angular_core.WritableSignal<number>;
246
+ readonly pageSize: _angular_core.WritableSignal<number>;
247
+ readonly totalPages: _angular_core.Signal<number>;
248
+ status: string;
249
+ search: string;
250
+ ngOnInit(): void;
251
+ statusLabel(status: BusinessVerificationStatus): string;
252
+ badge(status: BusinessVerificationStatus): string;
253
+ reason(reason: string): string;
254
+ reload(): void;
255
+ goTo(page: number): void;
256
+ resize(size: number): void;
257
+ open(business: VerifiedBusinessSummary): void;
258
+ private load;
259
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<BusinessesPageComponent, never>;
260
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<BusinessesPageComponent, "verification-businesses-page", never, {}, {}, never, never, true, never>;
261
+ }
262
+
263
+ interface PendingReview {
264
+ target: 'business' | 'applicant';
265
+ id: string;
266
+ decision: ReviewDecision;
267
+ }
268
+ declare class BusinessDetailPageComponent implements OnInit {
269
+ private readonly service;
270
+ private readonly toast;
271
+ private readonly permissions;
272
+ private readonly route;
273
+ readonly loading: _angular_core.WritableSignal<boolean>;
274
+ readonly business: _angular_core.WritableSignal<VerifiedBusinessDetail | null>;
275
+ readonly pending: _angular_core.WritableSignal<PendingReview | null>;
276
+ readonly reviewing: _angular_core.WritableSignal<boolean>;
277
+ readonly refreshing: _angular_core.WritableSignal<string | null>;
278
+ readonly downloading: _angular_core.WritableSignal<string | null>;
279
+ readonly canReview: _angular_core.Signal<boolean>;
280
+ readonly dialogTitle: _angular_core.Signal<string>;
281
+ readonly dialogMessage: _angular_core.Signal<"" | "Mark the business as verified. People waiting on it are verified when the step asked for nothing more, otherwise they move to review." | "Reject the business. Everyone waiting on it is rejected; webhooks and workflows receive the decision." | "Let this person act for the business. Webhooks and workflows receive the decision and the applicant is emailed if they asked." | "Refuse this person. Webhooks and workflows receive the decision and the applicant is emailed if they asked.">;
282
+ ngOnInit(): void;
283
+ statusLabel(status: BusinessVerificationStatus): string;
284
+ badge(status: BusinessVerificationStatus): string;
285
+ reason(reason: string): string;
286
+ relationship(relationship: string): string;
287
+ kind(kind: BusinessCheck['kind']): string;
288
+ match(value: boolean | null | undefined): string;
289
+ size(bytes: number): string;
290
+ ask(target: 'business' | 'applicant', id: string, decision: ReviewDecision): void;
291
+ review(note: string): void;
292
+ refresh(applicant: BusinessApplicant): void;
293
+ openDocument(applicant: BusinessApplicant, document: BusinessDocument): void;
294
+ private load;
295
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<BusinessDetailPageComponent, never>;
296
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<BusinessDetailPageComponent, "verification-business-detail-page", never, {}, {}, never, never, true, never>;
297
+ }
298
+
299
+ declare class ReviewQueuePageComponent implements OnInit {
300
+ private readonly service;
301
+ private readonly toast;
302
+ readonly loading: _angular_core.WritableSignal<boolean>;
303
+ readonly items: _angular_core.WritableSignal<BusinessReviewQueueItem[]>;
304
+ readonly total: _angular_core.WritableSignal<number>;
305
+ readonly page: _angular_core.WritableSignal<number>;
306
+ readonly pageSize: _angular_core.WritableSignal<number>;
307
+ readonly totalPages: _angular_core.Signal<number>;
308
+ ngOnInit(): void;
309
+ statusLabel(status: BusinessVerificationStatus): string;
310
+ badge(status: BusinessVerificationStatus): string;
311
+ reason(reason: string): string;
312
+ relationship(relationship: string): string;
313
+ goTo(page: number): void;
314
+ resize(size: number): void;
315
+ private load;
316
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ReviewQueuePageComponent, never>;
317
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<ReviewQueuePageComponent, "verification-review-queue-page", never, {}, {}, never, never, true, never>;
318
+ }
319
+
320
+ export { BusinessDetailPageComponent, BusinessesPageComponent, REASON_LABELS, RELATIONSHIP_LABELS, ReviewQueuePageComponent, STATUS_CLASSES, STATUS_LABELS, VERIFICATION_PLUGIN, VERIFICATION_ROUTES, VerificationService, VerificationSettingsTabComponent };
321
+ export type { BusinessApplicant, BusinessCheck, BusinessDocument, BusinessPerson, BusinessReviewQueue, BusinessReviewQueueItem, BusinessVerificationCountry, BusinessVerificationStatus, ConfiguredVerificationProvider, IdentityVerificationSettings, ReviewDecision, UpdateIdentityVerificationSettingsRequest, UpdateVerificationProviderRequest, VerificationConnectionTestResult, VerificationCredentialField, VerificationEnvironment, VerificationProviderInfo, VerificationProviderOption, VerifiedBusinessDetail, VerifiedBusinessList, VerifiedBusinessSummary };
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.