/**
* Dashboard Manager for Lark aPaaS
*
* IMPORTANT: This is a workaround implementation since Lark does not provide
* a public API for creating dashboard views programmatically.
*
* Dashboard views MUST be created manually in the Lark UI. This manager
* provides utilities to:
* 1. Manage data that feeds into dashboards
* 2. Prepare tables and views for manual dashboard creation
* 3. Validate dashboard configurations
* 4. Provide guided setup instructions
*
* @see docs/APAAS_API_RESEARCH.md for full analysis
*/
import { LarkBaseClient } from '../api';
export interface DashboardConfig {
/**
* Name of the dashboard (for documentation purposes)
* Note: Dashboard must be created manually in Lark UI
*/
dashboardName: string;
/**
* Description of what the dashboard shows
*/
description?: string;
/**
* Source tables that feed data into the dashboard
*/
dataSources: Array<{
tableId: string;
tableName: string;
viewId?: string; // Optional: specific view to use as data source
}>;
/**
* Recommended chart types for this dashboard
*/
recommendedCharts?: Array<{
type: 'bar' | 'line' | 'pie' | 'scatter' | 'table' | 'funnel';
title: string;
dataSource: string; // tableId or tableName
xField?: string;
yField?: string;
groupBy?: string;
}>;
/**
* Filters to apply to dashboard
*/
filters?: Array<{
field: string;
operator: 'is' | 'isNot' | 'contains' | 'greaterThan' | 'lessThan';
value: any;
}>;
}
export interface SetupInstructions {
steps: Array<{
stepNumber: number;
action: string;
details?: string;
automated: boolean;
}>;
manualSteps: number;
automatedSteps: number;
estimatedTime: string;
}
/**
* Dashboard Manager
*
* Manages dashboard data and provides setup instructions for manual dashboard creation
*/
export class DashboardManager {
private client: LarkBaseClient;
private appToken: string;
constructor(client: LarkBaseClient, appToken: string) {
this.client = client;
this.appToken = appToken;
}
/**
* Prepare infrastructure for a dashboard
*
* Creates all necessary tables and views that will feed data into the dashboard.
* The dashboard itself must still be created manually in Lark UI.
*
* @param config Dashboard configuration
* @returns Setup instructions for manual dashboard creation
*/
async prepareDashboardInfrastructure(
config: DashboardConfig
): Promise<SetupInstructions> {
const steps: SetupInstructions['steps'] = [];
let stepNumber = 1;
// Step 1: Validate data sources
steps.push({
stepNumber: stepNumber++,
action: 'Validating data sources',
automated: true
});
await this.validateDataSources(config.dataSources);
// Step 2: Create recommended views
steps.push({
stepNumber: stepNumber++,
action: 'Creating recommended grid views for data sources',
automated: true
});
for (const source of config.dataSources) {
// Create a grid view optimized for dashboard consumption
try {
await this.client.views.create(source.tableId, {
view_name: `${source.tableName} - Dashboard Source`,
view_type: 'grid'
});
} catch (error) {
console.warn(`View may already exist for ${source.tableName}`);
}
}
// Manual steps
steps.push({
stepNumber: stepNumber++,
action: 'CREATE DASHBOARD MANUALLY',
details: this.generateManualDashboardInstructions(config),
automated: false
});
steps.push({
stepNumber: stepNumber++,
action: 'Add charts to dashboard',
details: this.generateChartInstructions(config),
automated: false
});
steps.push({
stepNumber: stepNumber++,
action: 'Configure dashboard filters',
details: this.generateFilterInstructions(config),
automated: false
});
// Final step: Verify
steps.push({
stepNumber: stepNumber++,
action: 'Verify dashboard is working',
details: 'Refresh the page and check that all charts display correctly',
automated: false
});
const manualSteps = steps.filter(s => !s.automated).length;
const automatedSteps = steps.filter(s => s.automated).length;
return {
steps,
manualSteps,
automatedSteps,
estimatedTime: this.estimateSetupTime(manualSteps)
};
}
/**
* Update dashboard data
*
* Updates the underlying tables that feed into the dashboard.
* Dashboard will automatically refresh with new data.
*/
async updateDashboardData(
tableId: string,
records: Array<{ fields: Record<string, any> }>
): Promise<void> {
await this.client.records.batchCreate(tableId, records);
}
/**
* Validate that all data sources exist
*/
private async validateDataSources(
sources: DashboardConfig['dataSources']
): Promise<void> {
const tables = await this.client.tables.list();
const tableIds = new Set(tables.map((t: { table_id: string }) => t.table_id));
for (const source of sources) {
if (!tableIds.has(source.tableId)) {
throw new Error(
`Data source table not found: ${source.tableName} (${source.tableId})`
);
}
}
}
/**
* Generate manual instructions for dashboard creation
*/
private generateManualDashboardInstructions(config: DashboardConfig): string {
return `
MANUAL DASHBOARD CREATION REQUIRED
Lark does not provide an API for creating dashboard views.
Follow these steps in the Lark UI:
1. Open your Base app: https://hypelive.sg.larksuite.com/app/${this.appToken}
2. Click the "+" button next to view tabs
3. Select "Dashboard" from the view type dropdown
4. Name it: "${config.dashboardName}"
${config.description ? `5. Add description: "${config.description}"` : ''}
Once created, proceed to add charts in the next step.
`.trim();
}
/**
* Generate chart configuration instructions
*/
private generateChartInstructions(config: DashboardConfig): string {
if (!config.recommendedCharts || config.recommendedCharts.length === 0) {
return 'Add charts manually based on your needs.';
}
let instructions = 'Add the following charts:\n\n';
config.recommendedCharts.forEach((chart, index) => {
instructions += `Chart ${index + 1}: ${chart.title}\n`;
instructions += ` - Type: ${chart.type}\n`;
instructions += ` - Data Source: ${chart.dataSource}\n`;
if (chart.xField) instructions += ` - X-Axis: ${chart.xField}\n`;
if (chart.yField) instructions += ` - Y-Axis: ${chart.yField}\n`;
if (chart.groupBy) instructions += ` - Group By: ${chart.groupBy}\n`;
instructions += '\n';
});
return instructions;
}
/**
* Generate filter configuration instructions
*/
private generateFilterInstructions(config: DashboardConfig): string {
if (!config.filters || config.filters.length === 0) {
return 'No filters configured.';
}
let instructions = 'Configure these filters:\n\n';
config.filters.forEach((filter, index) => {
instructions += `Filter ${index + 1}:\n`;
instructions += ` - Field: ${filter.field}\n`;
instructions += ` - Operator: ${filter.operator}\n`;
instructions += ` - Value: ${filter.value}\n\n`;
});
return instructions;
}
/**
* Estimate setup time based on complexity
*/
private estimateSetupTime(manualSteps: number): string {
const minutesPerStep = 5;
const totalMinutes = manualSteps * minutesPerStep;
if (totalMinutes < 60) {
return `${totalMinutes} minutes`;
} else {
const hours = Math.floor(totalMinutes / 60);
const minutes = totalMinutes % 60;
return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;
}
}
/**
* Print setup instructions to console
*/
printSetupInstructions(instructions: SetupInstructions): void {
console.log('\n='.repeat(80));
console.log('DASHBOARD SETUP INSTRUCTIONS');
console.log('='.repeat(80));
console.log();
console.log(`Estimated Time: ${instructions.estimatedTime}`);
console.log(`Automated Steps: ${instructions.automatedSteps}`);
console.log(`Manual Steps: ${instructions.manualSteps}`);
console.log();
console.log('STEPS:');
console.log('-'.repeat(80));
instructions.steps.forEach(step => {
const prefix = step.automated ? '✅ [AUTO]' : '⚠️ [MANUAL]';
console.log(`\n${prefix} Step ${step.stepNumber}: ${step.action}`);
if (step.details) {
console.log();
console.log(step.details);
}
});
console.log();
console.log('='.repeat(80));
console.log();
}
}
/**
* Helper function to create a dashboard manager instance
*/
export function createDashboardManager(
client: LarkBaseClient,
appToken: string
): DashboardManager {
return new DashboardManager(client, appToken);
}
/**
* Example usage:
*
* ```typescript
* import { LarkBaseClient } from './api';
* import { createDashboardManager } from './apaas/DashboardManager';
*
* const client = new LarkBaseClient({
* appId: 'cli_xxx',
* appSecret: 'xxx',
* appToken: 'KdW3bZ3axa636XsocGVln8DAgmg'
* });
*
* const dashboardMgr = createDashboardManager(
* client,
* 'KdW3bZ3axa636XsocGVln8DAgmg'
* );
*
* // Prepare dashboard infrastructure
* const instructions = await dashboardMgr.prepareDashboardInfrastructure({
* dashboardName: 'TikTok Campaign Performance',
* description: 'Real-time analytics for TikTok advertising campaigns',
* dataSources: [
* { tableId: 'tblXXX', tableName: 'Campaigns' },
* { tableId: 'tblYYY', tableName: 'Ad Groups' }
* ],
* recommendedCharts: [
* {
* type: 'bar',
* title: 'Campaign Performance',
* dataSource: 'Campaigns',
* xField: 'Campaign Name',
* yField: 'Conversions'
* }
* ]
* });
*
* // Print instructions
* dashboardMgr.printSetupInstructions(instructions);
*
* // Update data (dashboard auto-refreshes)
* await dashboardMgr.updateDashboardData('tblXXX', [
* {
* fields: {
* 'Campaign Name': 'Q1 Campaign',
* 'Impressions': 1000000,
* 'Conversions': 250
* }
* }
* ]);
* ```
*/