feat: Runtime configuration and Docker deployment improvements

Frontend:
- Add runtime configuration service for deployment-time API URL injection
- Create docker-entrypoint.sh to generate config.json from environment variables
- Update ApiService, ApprovalService, and DocumentViewer to use RuntimeConfigService
- Add APP_INITIALIZER to load runtime config before app starts

Backend:
- Fix init-blockchain.js to properly quote mnemonic phrases in .env file
- Improve docker-entrypoint.sh with health checks and better error handling

Docker:
- Add API_BASE_URL environment variable to frontend container
- Update docker-compose.yml with clear documentation for remote deployment
- Reorganize .env.example with clear categories (REQUIRED FOR REMOTE, PRODUCTION, AUTO-GENERATED)

Workflow fixes:
- Fix DepartmentApproval interface to match backend schema
- Fix stage transformation for 0-indexed stageOrder
- Fix workflow list to show correct stage count from definition.stages

Cleanup:
- Move development artifacts to .trash directory
- Remove root-level package.json (was only for utility scripts)
- Add .trash/ to .gitignore
This commit is contained in:
Mahi
2026-02-08 18:44:05 -04:00
parent 2c10cd5662
commit d9de183e51
171 changed files with 10236 additions and 8386 deletions

View File

@@ -7,11 +7,14 @@ import { MatTooltipModule } from '@angular/material/tooltip';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatTabsModule } from '@angular/material/tabs';
import { MatChipsModule } from '@angular/material/chips';
import { MatDialogModule, MatDialog } from '@angular/material/dialog';
import { RouterModule } from '@angular/router';
import { Clipboard } from '@angular/cdk/clipboard';
import { interval, Subscription } from 'rxjs';
import { ApiService } from '../../../core/services/api.service';
import { BlockDto, BlockchainTransactionDto } from '../../../api/models';
import { BlockDetailDialogComponent } from './block-detail-dialog.component';
import { TransactionDetailDialogComponent } from './transaction-detail-dialog.component';
@Component({
selector: 'app-blockchain-explorer-mini',
@@ -25,6 +28,7 @@ import { BlockDto, BlockchainTransactionDto } from '../../../api/models';
MatProgressSpinnerModule,
MatTabsModule,
MatChipsModule,
MatDialogModule,
RouterModule,
],
template: `
@@ -240,18 +244,17 @@ import { BlockDto, BlockchainTransactionDto } from '../../../api/models';
}
}
.header-text {
h3 {
margin: 0;
font-size: 1.125rem;
font-weight: 600;
}
.header-text h3 {
margin: 0;
font-size: 1.125rem;
font-weight: 600;
color: white !important;
}
.subtitle {
margin: 4px 0 0;
font-size: 0.8rem;
opacity: 0.8;
}
.header-text .subtitle {
margin: 4px 0 0;
font-size: 0.8rem;
color: rgba(255, 255, 255, 0.9) !important;
}
.header-right {
@@ -598,6 +601,7 @@ import { BlockDto, BlockchainTransactionDto } from '../../../api/models';
export class BlockchainExplorerMiniComponent implements OnInit, OnDestroy {
private readonly api = inject(ApiService);
private readonly clipboard = inject(Clipboard);
private readonly dialog = inject(MatDialog);
private refreshSubscription?: Subscription;
@Input() showViewAll = true;
@@ -655,11 +659,12 @@ export class BlockchainExplorerMiniComponent implements OnInit, OnDestroy {
{ limit: 5 }
).toPromise();
if (blocksResponse?.data) {
if (blocksResponse?.data && blocksResponse.data.length > 0) {
this.blocks.set(blocksResponse.data);
if (blocksResponse.data.length > 0) {
this.latestBlock.set(blocksResponse.data[0].blockNumber);
}
this.latestBlock.set(blocksResponse.data[0].blockNumber);
} else {
// No real blocks - use mock data for demo
this.loadMockBlocks();
}
// Fetch transactions
@@ -687,18 +692,24 @@ export class BlockchainExplorerMiniComponent implements OnInit, OnDestroy {
}
}
private loadMockData(): void {
// Mock blocks
private loadMockBlocks(): void {
const mockBlocks: BlockDto[] = Array.from({ length: 5 }, (_, i) => ({
blockNumber: 12345 - i,
blockNumber: 1628260 - i,
hash: `0x${this.generateRandomHash()}`,
parentHash: `0x${this.generateRandomHash()}`,
timestamp: new Date(Date.now() - i * 15000).toISOString(),
transactionCount: Math.floor(Math.random() * 20) + 1,
gasUsed: Math.floor(Math.random() * 8000000),
transactionCount: Math.floor(Math.random() * 5) + 1,
gasUsed: Math.floor(Math.random() * 500000) + 100000,
gasLimit: 15000000,
size: Math.floor(Math.random() * 50000) + 10000,
}));
this.blocks.set(mockBlocks);
this.latestBlock.set(mockBlocks[0].blockNumber);
}
private loadMockData(): void {
// Mock blocks
this.loadMockBlocks();
const mockTx: BlockchainTransactionDto[] = [
{
@@ -746,9 +757,7 @@ export class BlockchainExplorerMiniComponent implements OnInit, OnDestroy {
},
];
this.blocks.set(mockBlocks);
this.transactions.set(mockTx);
this.latestBlock.set(mockBlocks[0].blockNumber);
this.totalTransactions.set(1234);
this.pendingTransactions.set(3);
this.networkStatus.set('HEALTHY');
@@ -761,11 +770,12 @@ export class BlockchainExplorerMiniComponent implements OnInit, OnDestroy {
}
truncateHash(hash: string): string {
if (!hash || hash.length <= 18) return hash;
if (!hash || hash.length <= 18) return hash || '';
return `${hash.substring(0, 10)}...${hash.substring(hash.length - 6)}`;
}
getRelativeTime(timestamp: string): string {
if (!timestamp) return '';
const now = new Date();
const time = new Date(timestamp);
const diffSeconds = Math.floor((now.getTime() - time.getTime()) / 1000);
@@ -785,6 +795,7 @@ export class BlockchainExplorerMiniComponent implements OnInit, OnDestroy {
}
getTxTypeIcon(type: string): string {
if (!type) return 'receipt_long';
const icons: Record<string, string> = {
LICENSE_MINT: 'verified',
DOCUMENT_HASH: 'fingerprint',
@@ -796,6 +807,7 @@ export class BlockchainExplorerMiniComponent implements OnInit, OnDestroy {
}
formatTxType(type: string): string {
if (!type) return 'Unknown';
return type.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
}
@@ -805,12 +817,20 @@ export class BlockchainExplorerMiniComponent implements OnInit, OnDestroy {
}
viewBlock(block: BlockDto): void {
// Could open a dialog or navigate
console.log('View block:', block);
this.dialog.open(BlockDetailDialogComponent, {
data: block,
width: '600px',
maxHeight: '90vh',
panelClass: 'blockchain-detail-dialog',
});
}
viewTransaction(tx: BlockchainTransactionDto): void {
// Could open a dialog or navigate
console.log('View transaction:', tx);
this.dialog.open(TransactionDetailDialogComponent, {
data: tx,
width: '600px',
maxHeight: '90vh',
panelClass: 'blockchain-detail-dialog',
});
}
}