Initial BlackRoad OS Hub - Meta-CRM Platform

## Hub Layer
- Connected_CRM__c: Manage multiple CRM instances
- CRM_Product__c: CRM product templates

## Financial Advisor CRM
- Client_Household__c: Unified household view
- Financial_Account__c: IRA, brokerage, annuity tracking
- Distribution_Request__c: Withdrawal workflows
- Mortality_Event__c: Estate processing
- Liquidity_Event__c: Business sales, large transfers
- Compliance_Log__c: FINRA audit trail

## Components
- BlackRoadHubController: Hub dashboard controller
- FinancialAdvisorService: FA business logic
- blackroadHubDashboard: Lightning Web Component
- BlackRoad Hub app with all tabs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Alexa Louise
2026-01-11 16:26:21 -06:00
commit ee7e9aff64
94 changed files with 1805 additions and 0 deletions

View File

@@ -0,0 +1,31 @@
.blackroad-header {
background: linear-gradient(135deg, #000000 0%, #1a1a2e 100%);
color: white;
border-radius: 8px;
margin-bottom: 1rem;
}
.blackroad-header .slds-page-header__title {
color: #F5A623;
}
.blackroad-header .slds-page-header__meta-text {
color: #ffffff;
}
.stat-number {
font-size: 2.5rem;
font-weight: bold;
color: #F5A623;
line-height: 1.2;
}
.stat-label {
font-size: 0.875rem;
color: #706e6b;
margin-top: 0.25rem;
}
lightning-card {
--slds-c-card-radius-border: 8px;
}

View File

@@ -0,0 +1,129 @@
<template>
<div class="slds-page-header blackroad-header">
<div class="slds-page-header__row">
<div class="slds-page-header__col-title">
<div class="slds-media">
<div class="slds-media__figure">
<lightning-icon icon-name="custom:custom9" size="large"></lightning-icon>
</div>
<div class="slds-media__body">
<h1 class="slds-page-header__title">BlackRoad OS Hub</h1>
<p class="slds-page-header__meta-text">CRM Command Center</p>
</div>
</div>
</div>
</div>
</div>
<!-- Stats Cards -->
<div class="slds-grid slds-gutters slds-m-top_medium">
<div class="slds-col slds-size_1-of-4">
<lightning-card title="Connected CRMs" icon-name="standard:connected_apps">
<div class="slds-p-horizontal_medium">
<p class="stat-number">{stats.activeCRMs} / {stats.totalCRMs}</p>
<p class="stat-label">Active</p>
</div>
</lightning-card>
</div>
<div class="slds-col slds-size_1-of-4">
<lightning-card title="Total Households" icon-name="standard:household">
<div class="slds-p-horizontal_medium">
<p class="stat-number">{stats.totalHouseholds}</p>
<p class="stat-label">Client Households</p>
</div>
</lightning-card>
</div>
<div class="slds-col slds-size_1-of-4">
<lightning-card title="Total AUM" icon-name="standard:currency">
<div class="slds-p-horizontal_medium">
<p class="stat-number">{formattedAUM}</p>
<p class="stat-label">Assets Under Management</p>
</div>
</lightning-card>
</div>
<div class="slds-col slds-size_1-of-4">
<lightning-card title="Pending Actions" icon-name="standard:task">
<div class="slds-p-horizontal_medium">
<p class="stat-number">{totalPending}</p>
<p class="stat-label">Distributions / Events</p>
</div>
</lightning-card>
</div>
</div>
<!-- Main Content Grid -->
<div class="slds-grid slds-gutters slds-m-top_medium">
<!-- Connected CRMs -->
<div class="slds-col slds-size_1-of-2">
<lightning-card title="Connected CRMs" icon-name="standard:connected_apps">
<lightning-button slot="actions" label="Add CRM" onclick={handleAddCRM}></lightning-button>
<template if:true={connectedCRMs}>
<lightning-datatable
key-field="Id"
data={connectedCRMs}
columns={crmColumns}
hide-checkbox-column
onrowaction={handleCRMAction}>
</lightning-datatable>
</template>
<template if:false={connectedCRMs}>
<div class="slds-p-around_medium slds-text-align_center">
<p>No CRMs connected yet.</p>
</div>
</template>
</lightning-card>
</div>
<!-- CRM Products -->
<div class="slds-col slds-size_1-of-2">
<lightning-card title="CRM Products" icon-name="standard:product">
<template if:true={crmProducts}>
<ul class="slds-has-dividers_bottom-space">
<template for:each={crmProducts} for:item="product">
<li key={product.Id} class="slds-item slds-p-around_small">
<div class="slds-grid slds-grid_vertical-align-center">
<div class="slds-col slds-grow">
<p class="slds-text-heading_small">{product.Name}</p>
<p class="slds-text-body_small slds-text-color_weak">{product.Target_Vertical__c}</p>
</div>
<div class="slds-col">
<lightning-badge label={product.Version__c}></lightning-badge>
</div>
</div>
</li>
</template>
</ul>
</template>
</lightning-card>
</div>
</div>
<!-- Households Needing Attention -->
<div class="slds-m-top_medium">
<lightning-card title="Households Needing Attention" icon-name="standard:warning">
<template if:true={householdsNeedingAttention}>
<lightning-datatable
key-field="Id"
data={householdsNeedingAttention}
columns={householdColumns}
hide-checkbox-column
onrowaction={handleHouseholdAction}>
</lightning-datatable>
</template>
</lightning-card>
</div>
<!-- Recent Compliance Activity -->
<div class="slds-m-top_medium">
<lightning-card title="Recent Compliance Activity" icon-name="standard:logging">
<template if:true={complianceLogs}>
<lightning-datatable
key-field="Id"
data={complianceLogs}
columns={logColumns}
hide-checkbox-column>
</lightning-datatable>
</template>
</lightning-card>
</div>
</template>

View File

@@ -0,0 +1,179 @@
import { LightningElement, wire, track } from 'lwc';
import { NavigationMixin } from 'lightning/navigation';
import getConnectedCRMs from '@salesforce/apex/BlackRoadHubController.getConnectedCRMs';
import getCRMProducts from '@salesforce/apex/BlackRoadHubController.getCRMProducts';
import getHubStats from '@salesforce/apex/BlackRoadHubController.getHubStats';
import getHouseholdsNeedingAttention from '@salesforce/apex/BlackRoadHubController.getHouseholdsNeedingAttention';
import getRecentComplianceLogs from '@salesforce/apex/BlackRoadHubController.getRecentComplianceLogs';
import syncCRM from '@salesforce/apex/BlackRoadHubController.syncCRM';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
export default class BlackroadHubDashboard extends NavigationMixin(LightningElement) {
@track stats = {};
@track connectedCRMs = [];
@track crmProducts = [];
@track householdsNeedingAttention = [];
@track complianceLogs = [];
crmColumns = [
{ label: 'Name', fieldName: 'Name', type: 'text' },
{ label: 'Type', fieldName: 'CRM_Type__c', type: 'text' },
{ label: 'Status', fieldName: 'Status__c', type: 'text' },
{ label: 'Vertical', fieldName: 'Vertical__c', type: 'text' },
{ label: 'Records', fieldName: 'Record_Count__c', type: 'number' },
{
type: 'action',
typeAttributes: { rowActions: [
{ label: 'Sync', name: 'sync' },
{ label: 'View', name: 'view' }
]}
}
];
householdColumns = [
{ label: 'Household', fieldName: 'Name', type: 'text' },
{ label: 'AUM', fieldName: 'Total_AUM__c', type: 'currency' },
{ label: 'Status', fieldName: 'Household_Status__c', type: 'text' },
{ label: 'Next Review', fieldName: 'Next_Review_Date__c', type: 'date' },
{
type: 'action',
typeAttributes: { rowActions: [
{ label: 'View', name: 'view' },
{ label: 'Schedule Review', name: 'schedule' }
]}
}
];
logColumns = [
{ label: 'Log #', fieldName: 'Name', type: 'text' },
{ label: 'Type', fieldName: 'Log_Type__c', type: 'text' },
{ label: 'Description', fieldName: 'Description__c', type: 'text' },
{ label: 'Date', fieldName: 'CreatedDate', type: 'date' }
];
@wire(getHubStats)
wiredStats({ error, data }) {
if (data) {
this.stats = data;
} else if (error) {
console.error('Error loading stats:', error);
}
}
@wire(getConnectedCRMs)
wiredCRMs({ error, data }) {
if (data) {
this.connectedCRMs = data;
} else if (error) {
console.error('Error loading CRMs:', error);
}
}
@wire(getCRMProducts)
wiredProducts({ error, data }) {
if (data) {
this.crmProducts = data;
} else if (error) {
console.error('Error loading products:', error);
}
}
@wire(getHouseholdsNeedingAttention)
wiredHouseholds({ error, data }) {
if (data) {
this.householdsNeedingAttention = data;
} else if (error) {
console.error('Error loading households:', error);
}
}
@wire(getRecentComplianceLogs, { recordLimit: 10 })
wiredLogs({ error, data }) {
if (data) {
this.complianceLogs = data;
} else if (error) {
console.error('Error loading logs:', error);
}
}
get formattedAUM() {
if (!this.stats.totalAUM) return '$0';
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 0,
maximumFractionDigits: 0
}).format(this.stats.totalAUM);
}
get totalPending() {
return (this.stats.pendingDistributions || 0) +
(this.stats.activeMortalityEvents || 0) +
(this.stats.activeLiquidityEvents || 0);
}
handleAddCRM() {
this[NavigationMixin.Navigate]({
type: 'standard__objectPage',
attributes: {
objectApiName: 'Connected_CRM__c',
actionName: 'new'
}
});
}
handleCRMAction(event) {
const action = event.detail.action;
const row = event.detail.row;
if (action.name === 'sync') {
this.syncCRMInstance(row.Id);
} else if (action.name === 'view') {
this[NavigationMixin.Navigate]({
type: 'standard__recordPage',
attributes: {
recordId: row.Id,
objectApiName: 'Connected_CRM__c',
actionName: 'view'
}
});
}
}
handleHouseholdAction(event) {
const action = event.detail.action;
const row = event.detail.row;
if (action.name === 'view') {
this[NavigationMixin.Navigate]({
type: 'standard__recordPage',
attributes: {
recordId: row.Id,
objectApiName: 'Client_Household__c',
actionName: 'view'
}
});
}
}
async syncCRMInstance(crmId) {
try {
const result = await syncCRM({ crmId: crmId });
this.dispatchEvent(
new ShowToastEvent({
title: 'Success',
message: result,
variant: 'success'
})
);
} catch (error) {
this.dispatchEvent(
new ShowToastEvent({
title: 'Error',
message: error.body.message,
variant: 'error'
})
);
}
}
}

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>59.0</apiVersion>
<isExposed>true</isExposed>
<masterLabel>BlackRoad Hub Dashboard</masterLabel>
<description>Main dashboard for BlackRoad OS Hub - CRM Command Center</description>
<targets>
<target>lightning__AppPage</target>
<target>lightning__HomePage</target>
<target>lightning__Tab</target>
</targets>
</LightningComponentBundle>